diff options
Diffstat (limited to 'gtk/tests')
-rw-r--r-- | gtk/tests/Makefile.am | 25 | ||||
-rw-r--r-- | gtk/tests/action.c | 1 | ||||
-rw-r--r-- | gtk/tests/filtermodel.c | 3424 | ||||
-rw-r--r-- | gtk/tests/gtktreemodelrefcount.c | 286 | ||||
-rw-r--r-- | gtk/tests/gtktreemodelrefcount.h | 134 | ||||
-rw-r--r-- | gtk/tests/liststore.c | 85 | ||||
-rw-r--r-- | gtk/tests/modelrefcount.c | 978 | ||||
-rw-r--r-- | gtk/tests/sortmodel.c | 1151 | ||||
-rw-r--r-- | gtk/tests/treemodel.c | 347 | ||||
-rw-r--r-- | gtk/tests/treemodel.h | 60 | ||||
-rw-r--r-- | gtk/tests/treestore.c | 117 |
11 files changed, 6025 insertions, 583 deletions
diff --git a/gtk/tests/Makefile.am b/gtk/tests/Makefile.am index ff5720d68e..4b2d3440ef 100644 --- a/gtk/tests/Makefile.am +++ b/gtk/tests/Makefile.am @@ -27,13 +27,18 @@ TEST_PROGS += testing testing_SOURCES = testing.c testing_LDADD = $(progs_ldadd) -TEST_PROGS += liststore -liststore_SOURCES = liststore.c -liststore_LDADD = $(progs_ldadd) - -TEST_PROGS += treestore -treestore_SOURCES = treestore.c -treestore_LDADD = $(progs_ldadd) +TEST_PROGS += treemodel +treemodel_SOURCES = \ + treemodel.h \ + treemodel.c \ + liststore.c \ + treestore.c \ + filtermodel.c \ + sortmodel.c \ + modelrefcount.c \ + gtktreemodelrefcount.h \ + gtktreemodelrefcount.c +treemodel_LDADD = $(progs_ldadd) TEST_PROGS += treeview treeview_SOURCES = treeview.c @@ -41,7 +46,7 @@ treeview_LDADD = $(progs_ldadd) TEST_PROGS += treeview-scrolling treeview_scrolling_SOURCES = treeview-scrolling.c -treeview_scrolling_LDADD = $(progs_ldadd) +treeview_scrolling_LDADD = $(progs_ldadd) -lm TEST_PROGS += recentmanager recentmanager_SOURCES = recentmanager.c @@ -86,10 +91,6 @@ TEST_PROGS += textiter textiter_SOURCES = textiter.c textiter_LDADD = $(progs_ldadd) -TEST_PROGS += filtermodel -filtermodel_SOURCES = filtermodel.c -filtermodel_LDADD = $(progs_ldadd) - TEST_PROGS += expander expander_SOURCES = expander.c expander_LDADD = $(progs_ldadd) diff --git a/gtk/tests/action.c b/gtk/tests/action.c index dffb5b18fe..6617d9521a 100644 --- a/gtk/tests/action.c +++ b/gtk/tests/action.c @@ -57,6 +57,7 @@ menu_item_label_notify_count (ActionTest *fixture, GtkWidget *item = gtk_menu_item_new (); unsigned int emmisions = 0; + g_object_ref_sink (item); g_signal_connect (item, "notify::label", G_CALLBACK (notify_count_emmisions), &emmisions); diff --git a/gtk/tests/filtermodel.c b/gtk/tests/filtermodel.c index 91daaf9f12..a0c11a8994 100644 --- a/gtk/tests/filtermodel.c +++ b/gtk/tests/filtermodel.c @@ -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 @@ -19,15 +19,23 @@ #include <gtk/gtk.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. */ @@ -91,271 +99,6 @@ create_tree_store (int depth, } /* - * 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)) - { - g_error ("Signal queue empty\n"); - 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=%d path=%s\n", 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 */ @@ -412,6 +155,23 @@ filter_test_setup_generic (FilterTest *fixture, } 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) { @@ -433,12 +193,28 @@ filter_test_setup_unfiltered (FilterTest *fixture, } 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) { 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) @@ -463,6 +239,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, @@ -530,26 +332,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); @@ -660,6 +484,8 @@ filter_test_teardown (FilterTest *fixture, { signal_monitor_free (fixture->monitor); + gtk_widget_destroy (fixture->tree_view); + g_object_unref (fixture->filter); g_object_unref (fixture->store); } @@ -733,6 +559,9 @@ check_filter_model_recurse (FilterTest *fixture, gtk_tree_path_copy (store_parent_path), tmp); } + else + /* Only when we do not recurse we need to free tmp */ + gtk_tree_path_free (tmp); gtk_tree_path_next (filter_parent_path); filter_has_next = gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->filter), &filter_iter); @@ -780,24 +609,26 @@ check_filter_model_with_root (FilterTest *fixture, static void check_level_length (GtkTreeModelFilter *filter, const gchar *level, - const int length) + const int expected_length) { if (!level) { - int l = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), NULL); - g_return_if_fail (l == length); + int model_length; + + model_length = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), NULL); + g_assert_cmpint (model_length, ==, expected_length); } else { - int l; + int model_length; gboolean retrieved_iter = FALSE; GtkTreeIter iter; retrieved_iter = gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (filter), &iter, level); g_return_if_fail (retrieved_iter); - l = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), &iter); - g_return_if_fail (l == length); + model_length = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), &iter); + g_assert_cmpint (model_length, ==, expected_length); } } @@ -908,6 +739,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); @@ -935,10 +819,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); @@ -952,13 +832,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); @@ -1101,6 +976,60 @@ filled_vroot_hide_child_levels (FilterTest *fixture, { GtkTreePath *path = (GtkTreePath *)user_data; + 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); + + 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 */ + set_path_visibility (fixture, "2:0:4", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, "0:3", 0); + + 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. However, 0:4 is not + * visible, so no signal should be emitted. + */ + 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 +filled_vroot_hide_child_levels_root_expanded (FilterTest *fixture, + gconstpointer user_data) +{ + GtkTreePath *path = (GtkTreePath *)user_data; + GtkTreePath *tmp_path; + + 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); @@ -1128,10 +1057,6 @@ filled_vroot_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, "2:0:4", TRUE); check_filter_model_with_root (fixture, path); @@ -1145,19 +1070,14 @@ filled_vroot_hide_child_levels (FilterTest *fixture, 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. - */ - 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) @@ -1177,9 +1097,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); @@ -1217,8 +1135,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 @@ -1233,6 +1149,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); @@ -1257,9 +1174,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); @@ -1451,9 +1366,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); @@ -1466,6 +1400,29 @@ 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"); set_path_visibility (fixture, "2:2", FALSE); @@ -1474,9 +1431,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); @@ -1493,6 +1447,40 @@ unfiltered_hide_single_multi_level (FilterTest *fixture, /* 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); + 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", FALSE); @@ -1502,9 +1490,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); @@ -1523,6 +1508,7 @@ unfiltered_hide_single_multi_level (FilterTest *fixture, } + static void unfiltered_vroot_hide_single (FilterTest *fixture, gconstpointer user_data) @@ -1537,11 +1523,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); @@ -1555,6 +1541,32 @@ unfiltered_vroot_hide_single_child (FilterTest *fixture, { 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; + 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", FALSE); @@ -1563,10 +1575,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); @@ -1585,6 +1593,43 @@ unfiltered_vroot_hide_single_multi_level (FilterTest *fixture, /* 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; + + /* This row is not shown, so its signal is not propagated */ + set_path_visibility (fixture, "2:2:2:2", FALSE); + 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", FALSE); @@ -1594,9 +1639,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); @@ -1614,8 +1656,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) @@ -1628,10 +1668,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); @@ -1643,6 +1681,35 @@ unfiltered_show_single_child (FilterTest *fixture, gconstpointer user_data) { + 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); + + /* 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, 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); + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 1); +} + +static void +unfiltered_show_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"); set_path_visibility (fixture, "2:2", TRUE); @@ -1651,10 +1718,7 @@ unfiltered_show_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, 3); + filter_test_append_refilter_signals (fixture, 2); filter_test_enable_filter (fixture); check_filter_model (fixture); @@ -1676,6 +1740,41 @@ unfiltered_show_single_multi_level (FilterTest *fixture, gconstpointer user_data) { + /* 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); + + /* We only expect signals for the first level */ + filter_test_append_refilter_signals (fixture, 1); + 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_show_single_multi_level_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. */ @@ -1690,10 +1789,7 @@ unfiltered_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 (fixture, 3); + filter_test_append_refilter_signals (fixture, 2); filter_test_enable_filter (fixture); check_filter_model (fixture); @@ -1711,7 +1807,6 @@ unfiltered_show_single_multi_level (FilterTest *fixture, check_level_length (fixture->filter, "0:0", 1); } - static void unfiltered_vroot_show_single (FilterTest *fixture, gconstpointer user_data) @@ -1729,7 +1824,7 @@ unfiltered_vroot_show_single (FilterTest *fixture, /* 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_append_refilter_signals_with_vroot (fixture, 2, path); filter_test_enable_filter (fixture); check_filter_model_with_root (fixture, path); @@ -1743,8 +1838,6 @@ unfiltered_vroot_show_single_child (FilterTest *fixture, { 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); @@ -1772,6 +1865,39 @@ unfiltered_vroot_show_single_child (FilterTest *fixture, } 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) @@ -1783,6 +1909,44 @@ unfiltered_vroot_show_single_multi_level (FilterTest *fixture, */ set_path_visibility (fixture, "2:2:2:2", TRUE); + 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); + check_level_length (fixture->filter, "2:2", LEVEL_LENGTH); + + /* 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); + 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); + 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); +} + +static void +unfiltered_vroot_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: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); @@ -1792,10 +1956,7 @@ 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); + filter_test_append_refilter_signals_with_vroot (fixture, 3, path); filter_test_enable_filter (fixture); check_filter_model_with_root (fixture, path); @@ -1813,6 +1974,1332 @@ unfiltered_vroot_show_single_multi_level (FilterTest *fixture, check_level_length (fixture->filter, "0:0", 1); } +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_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"); + + 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); + + 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 +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; + + /* 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_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"); + + 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_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 +filtered_rows_reordered_root_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; + + /* 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_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); + + 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_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; + + /* 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); + + 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); +} + +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); + + signal_monitor_assert_is_empty (monitor); + check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1); + + /* Insert child -- invisible */ + 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); +} + + + +static void +remove_node (void) +{ + GtkTreeIter iter, iter1, iter2, iter3; + GtkListStore *list; + GtkTreeModel *filter; + GtkWidget *view G_GNUC_UNUSED; + + list = gtk_list_store_new (1, G_TYPE_INT); + gtk_list_store_insert_with_values (list, &iter1, 0, 0, 1, -1); + gtk_list_store_insert_with_values (list, &iter, 1, 0, 2, -1); + gtk_list_store_insert_with_values (list, &iter, 2, 0, 3, -1); + gtk_list_store_insert_with_values (list, &iter, 3, 0, 4, -1); + gtk_list_store_insert_with_values (list, &iter, 4, 0, 5, -1); + gtk_list_store_insert_with_values (list, &iter, 5, 0, 6, -1); + gtk_list_store_insert_with_values (list, &iter2, 6, 0, 7, -1); + gtk_list_store_insert_with_values (list, &iter3, 7, 0, 8, -1); + + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (list), NULL); + view = gtk_tree_view_new_with_model (filter); + + gtk_list_store_remove (list, &iter1); + gtk_list_store_remove (list, &iter3); + gtk_list_store_remove (list, &iter2); + + gtk_widget_destroy (view); + g_object_unref (filter); + g_object_unref (list); +} + +static void +remove_node_vroot (void) +{ + GtkTreeIter parent, root; + GtkTreeIter iter, iter1, iter2, iter3; + GtkTreeStore *tree; + GtkTreeModel *filter; + GtkTreePath *path; + GtkWidget *view G_GNUC_UNUSED; + + tree = gtk_tree_store_new (1, G_TYPE_INT); + gtk_tree_store_insert_with_values (tree, &parent, NULL, 0, 0, 0, -1); + gtk_tree_store_insert_with_values (tree, &root, &parent, 0, 0, 0, -1); + + gtk_tree_store_insert_with_values (tree, &iter1, &root, 0, 0, 1, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 1, 0, 2, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 2, 0, 3, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 3, 0, 4, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 4, 0, 5, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 5, 0, 6, -1); + gtk_tree_store_insert_with_values (tree, &iter2, &root, 6, 0, 7, -1); + gtk_tree_store_insert_with_values (tree, &iter3, &root, 7, 0, 8, -1); + + path = gtk_tree_path_new_from_indices (0, 0, -1); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (tree), path); + gtk_tree_path_free (path); + + view = gtk_tree_view_new_with_model (filter); + + gtk_tree_store_remove (tree, &iter1); + gtk_tree_store_remove (tree, &iter3); + gtk_tree_store_remove (tree, &iter2); + + gtk_widget_destroy (view); + g_object_unref (filter); + g_object_unref (tree); +} + +static void +remove_vroot_ancestor (void) +{ + GtkTreeIter parent, root; + GtkTreeIter iter, iter1, iter2, iter3; + GtkTreeStore *tree; + GtkTreeModel *filter; + GtkTreePath *path; + GtkWidget *view G_GNUC_UNUSED; + + tree = gtk_tree_store_new (1, G_TYPE_INT); + gtk_tree_store_insert_with_values (tree, &parent, NULL, 0, 0, 0, -1); + gtk_tree_store_insert_with_values (tree, &root, &parent, 0, 0, 0, -1); + + gtk_tree_store_insert_with_values (tree, &iter1, &root, 0, 0, 1, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 1, 0, 2, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 2, 0, 3, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 3, 0, 4, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 4, 0, 5, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 5, 0, 6, -1); + gtk_tree_store_insert_with_values (tree, &iter2, &root, 6, 0, 7, -1); + gtk_tree_store_insert_with_values (tree, &iter3, &root, 7, 0, 8, -1); + + path = gtk_tree_path_new_from_indices (0, 0, -1); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (tree), path); + gtk_tree_path_free (path); + + view = gtk_tree_view_new_with_model (filter); + + gtk_tree_store_remove (tree, &parent); + + gtk_widget_destroy (view); + g_object_unref (filter); + 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)); + + /* Only the reference on the first node of the root level is kept. */ + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 0); + assert_node_ref_count (ref_model, &iter_first, 0); + 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)); + + /* Only the reference on the first node of the root level is kept. */ + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &parent1, 0); + 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_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)); + + /* Only the reference on the first node of the root level is kept. */ + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &parent1, 0); + 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)); + + /* Only the reference on the first node of the root level is kept. */ + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &parent1, 0); + + 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_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_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_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 gboolean specific_path_dependent_filter_func (GtkTreeModel *model, @@ -1866,6 +3353,10 @@ specific_path_dependent_filter (void) NULL, 2)) gtk_list_store_remove (list, &iter); } + + g_object_unref (filter); + g_object_unref (sort); + g_object_unref (list); } @@ -2078,6 +3569,8 @@ specific_sort_filter_remove_root (void) sort = gtk_tree_model_sort_new_with_model (model); filter = gtk_tree_model_filter_new (sort, path); + gtk_tree_path_free (path); + gtk_tree_store_remove (GTK_TREE_STORE (model), &root); g_object_unref (filter); @@ -2138,13 +3631,15 @@ specific_has_child_filter (void) { GtkTreeModel *filter; GtkTreeIter iter, root; - /* A bit nasty, apologies */ - FilterTest fixture; + 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 = NULL; + 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 @@ -2158,11 +3653,20 @@ specific_has_child_filter (void) 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); @@ -2173,12 +3677,27 @@ specific_has_child_filter (void) */ 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); @@ -2188,14 +3707,19 @@ specific_has_child_filter (void) 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); @@ -2205,6 +3729,7 @@ specific_has_child_filter (void) set_path_visibility (&fixture, "0", FALSE); /* check_filter_model (&fixture); */ + signal_monitor_assert_is_empty (fixture.monitor); } @@ -2231,17 +3756,21 @@ specific_root_has_child_filter (void) { GtkTreeModel *filter; GtkTreeIter iter, root; - /* A bit nasty, apologies */ - FilterTest fixture; + FilterTest fixture; /* This is not how it should be done ... */ + GtkWidget *tree_view; - /* This is a variation on the above test case wherein the has-child - * check for visibility only applies to root level nodes. + /* 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 = NULL; + 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 @@ -2255,14 +3784,34 @@ specific_root_has_child_filter (void) 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 @@ -2271,37 +3820,375 @@ specific_root_has_child_filter (void) 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); + + /* 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); + + /* 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"); + 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"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1"); + 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 */ 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. + */ + set_path_visibility (&fixture, "0:0", FALSE); + signal_monitor_assert_is_empty (fixture.monitor); /* 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); + + /* 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); +} + +static 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); + 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); + + /* 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"); + + 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_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); +} + +static gboolean +specific_at_least_2_children_filter_filter_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + return gtk_tree_model_iter_n_children (model, iter) >= 2; +} + +static void +specific_at_least_2_children_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); + + 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. + */ + 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); + + /* 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); + + /* 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); + + /* 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", 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); +} + +static void +specific_at_least_2_children_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); + 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. + */ + 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); + GtkTreeRowReference *ref; + + 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); } @@ -2365,43 +4252,150 @@ specific_list_store_clear (void) } static void -specific_bug_300089 (void) +specific_sort_ref_leaf_and_remove_ancestor (void) { - /* Test case for GNOME Bugzilla bug 300089. Written by - * Matthias Clasen. - */ - GtkTreeModel *sort_model, *child_model; + GtkTreeIter iter, child, child2, child3; + GtkTreeStore *tree; + GtkTreeModel *sort; GtkTreePath *path; - GtkTreeIter iter, iter2, sort_iter; + GtkTreeRowReference *rowref; + GtkWidget *view G_GNUC_UNUSED; - child_model = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_STRING)); + tree = gtk_tree_store_new (1, G_TYPE_INT); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 0, 0, 1, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 1, 0, 2, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 2, 0, 3, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 3, 0, 4, -1); - gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL); - gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "A", -1); - gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL); - gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "B", -1); + gtk_tree_store_insert_with_values (tree, &child, &iter, 0, 0, 50, -1); + gtk_tree_store_insert_with_values (tree, &child2, &child, 0, 0, 6, -1); + gtk_tree_store_insert_with_values (tree, &child3, &child2, 0, 0, 7, -1); - gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter2, &iter); - gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter2, 0, "D", -1); - gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter2, &iter); - gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter2, 0, "E", -1); + sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (tree)); + view = gtk_tree_view_new_with_model (sort); + gtk_tree_view_expand_all (GTK_TREE_VIEW (view)); - gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL); - gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "C", -1); + path = gtk_tree_path_new_from_indices (3, 0, 0, 0, -1); + rowref = gtk_tree_row_reference_new (sort, path); + gtk_tree_path_free (path); + path = gtk_tree_path_new_from_indices (3, 0, 0, 0, -1); + rowref = gtk_tree_row_reference_new (sort, path); + gtk_tree_path_free (path); - sort_model = GTK_TREE_MODEL (gtk_tree_model_sort_new_with_model (child_model)); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), - 0, GTK_SORT_ASCENDING); + path = gtk_tree_path_new_from_indices (3, 0, -1); + rowref = gtk_tree_row_reference_new (sort, path); + gtk_tree_path_free (path); - path = gtk_tree_path_new_from_indices (1, 1, -1); + path = gtk_tree_path_new_from_indices (3, -1); + rowref = gtk_tree_row_reference_new (sort, path); + gtk_tree_path_free (path); - /* make sure a level is constructed */ - gtk_tree_model_get_iter (sort_model, &sort_iter, path); + /* Deleting a parent */ + path = gtk_tree_path_new_from_indices (3, 0, -1); + gtk_tree_model_get_iter (GTK_TREE_MODEL (tree), &iter, path); + gtk_tree_store_remove (tree, &iter); + gtk_tree_path_free (path); - /* change the "E" row in a way that causes it to change position */ - gtk_tree_model_get_iter (child_model, &iter, path); - gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "A", -1); + gtk_tree_row_reference_free (rowref); +} + +static void +specific_ref_leaf_and_remove_ancestor (void) +{ + GtkTreeIter iter, child, child2, child3; + GtkTreeStore *tree; + GtkTreeModel *filter; + GtkTreePath *path; + GtkTreeRowReference *rowref; + GtkWidget *view G_GNUC_UNUSED; + + tree = gtk_tree_store_new (1, G_TYPE_INT); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 0, 0, 1, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 1, 0, 2, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 2, 0, 3, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 3, 0, 4, -1); + + gtk_tree_store_insert_with_values (tree, &child, &iter, 0, 0, 50, -1); + gtk_tree_store_insert_with_values (tree, &child2, &child, 0, 0, 6, -1); + gtk_tree_store_insert_with_values (tree, &child3, &child2, 0, 0, 7, -1); + + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (tree), NULL); + view = gtk_tree_view_new_with_model (filter); + gtk_tree_view_expand_all (GTK_TREE_VIEW (view)); + + path = gtk_tree_path_new_from_indices (3, 0, 0, 0, -1); + rowref = gtk_tree_row_reference_new (filter, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (3, 0, 0, 0, -1); + rowref = gtk_tree_row_reference_new (filter, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (3, 0, -1); + rowref = gtk_tree_row_reference_new (filter, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (3, -1); + rowref = gtk_tree_row_reference_new (filter, path); + gtk_tree_path_free (path); + + /* Deleting a parent */ + path = gtk_tree_path_new_from_indices (3, 0, -1); + gtk_tree_model_get_iter (GTK_TREE_MODEL (tree), &iter, path); + gtk_tree_store_remove (tree, &iter); + gtk_tree_path_free (path); + + gtk_tree_row_reference_free (rowref); +} + +static void +specific_virtual_ref_leaf_and_remove_ancestor (void) +{ + GtkTreeIter iter, child, child2, child3; + GtkTreeStore *tree; + GtkTreeModel *filter; + GtkTreePath *path; + GtkTreeRowReference *rowref; + GtkWidget *view G_GNUC_UNUSED; + + tree = gtk_tree_store_new (1, G_TYPE_INT); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 0, 0, 1, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 1, 0, 2, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 2, 0, 3, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 3, 0, 4, -1); + + gtk_tree_store_insert_with_values (tree, &child, &iter, 0, 0, 50, -1); + gtk_tree_store_insert_with_values (tree, &child2, &child, 0, 0, 6, -1); + gtk_tree_store_insert_with_values (tree, &child3, &child2, 0, 0, 7, -1); + + /* Set a virtual root of 3:0 */ + path = gtk_tree_path_new_from_indices (3, 0, -1); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (tree), path); + gtk_tree_path_free (path); + + view = gtk_tree_view_new_with_model (filter); + gtk_tree_view_expand_all (GTK_TREE_VIEW (view)); + + path = gtk_tree_path_new_from_indices (0, 0, -1); + rowref = gtk_tree_row_reference_new (filter, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (0, 0, -1); + rowref = gtk_tree_row_reference_new (filter, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (0, -1); + rowref = gtk_tree_row_reference_new (filter, path); + gtk_tree_path_free (path); + + /* Deleting the virtual root */ + path = gtk_tree_path_new_from_indices (3, 0, -1); + gtk_tree_model_get_iter (GTK_TREE_MODEL (tree), &iter, path); + gtk_tree_store_remove (tree, &iter); + gtk_tree_path_free (path); + + gtk_tree_row_reference_free (rowref); } @@ -2433,6 +4427,8 @@ specific_bug_301558 (void) int i; gboolean add; + g_test_bug ("301558"); + tree = gtk_tree_store_new (2, G_TYPE_INT, G_TYPE_BOOLEAN); gtk_tree_store_append (tree, &iter, NULL); gtk_tree_store_set (tree, &iter, 0, 123, 1, TRUE, -1); @@ -2505,6 +4501,9 @@ specific_bug_311955 (void) GtkWidget *tree_view; int i; int n; + GtkTreePath *path; + + g_test_bug ("311955"); store = gtk_tree_store_new (1, G_TYPE_INT); @@ -2533,6 +4532,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++) { @@ -2553,6 +4555,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); @@ -2564,6 +4569,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 @@ -2587,6 +4672,8 @@ specific_bug_346800 (void) store = gtk_tree_store_newv (2, columns); model = GTK_TREE_MODEL (store); + g_test_bug ("346800"); + filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL)); gtk_tree_model_filter_set_visible_column (filter, 1); @@ -2622,45 +4709,6 @@ specific_bug_346800 (void) } } - -static void -specific_bug_364946 (void) -{ - /* This is a test case for GNOME Bugzilla bug 364946. It was written - * by Andreas Koehler. - */ - GtkTreeStore *store; - GtkTreeIter a, aa, aaa, aab, iter; - GtkTreeModel *s_model; - - store = gtk_tree_store_new (1, G_TYPE_STRING); - - gtk_tree_store_append (store, &a, NULL); - gtk_tree_store_set (store, &a, 0, "0", -1); - - gtk_tree_store_append (store, &aa, &a); - gtk_tree_store_set (store, &aa, 0, "0:0", -1); - - gtk_tree_store_append (store, &aaa, &aa); - gtk_tree_store_set (store, &aaa, 0, "0:0:0", -1); - - gtk_tree_store_append (store, &aab, &aa); - gtk_tree_store_set (store, &aab, 0, "0:0:1", -1); - - s_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store)); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (s_model), 0, - GTK_SORT_ASCENDING); - - gtk_tree_model_get_iter_from_string (s_model, &iter, "0:0:0"); - - gtk_tree_store_set (store, &aaa, 0, "0:0:0", -1); - gtk_tree_store_remove (store, &aaa); - gtk_tree_store_remove (store, &aab); - - gtk_tree_model_sort_clear_cache (GTK_TREE_MODEL_SORT (s_model)); -} - - static gboolean specific_bug_464173_visible_func (GtkTreeModel *model, GtkTreeIter *iter, @@ -2683,6 +4731,8 @@ specific_bug_464173 (void) GtkWidget *view G_GNUC_UNUSED; gboolean visible = TRUE; + g_test_bug ("464173"); + model = gtk_tree_store_new (1, G_TYPE_STRING); gtk_tree_store_append (model, &iter1, NULL); gtk_tree_store_set (model, &iter1, 0, "Foo", -1); @@ -2725,6 +4775,8 @@ specific_bug_540201 (void) GtkWidget *tree_view G_GNUC_UNUSED; + g_test_bug ("540201"); + store = gtk_tree_store_new (1, G_TYPE_INT); gtk_tree_store_append (store, &root, NULL); @@ -2776,6 +4828,8 @@ specific_bug_549287 (void) GtkTreeIter iter; GtkTreeIter *swap, *parent, *child; + g_test_bug ("529287"); + store = gtk_tree_store_new (1, G_TYPE_STRING); filtered = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filtered), @@ -2821,180 +4875,582 @@ specific_bug_549287 (void) } } -/* main */ +static gboolean +specific_bug_621076_visible_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gboolean visible = FALSE; + gchar *str = NULL; + + gtk_tree_model_get (model, iter, 0, &str, -1); + if (str != NULL && g_str_has_prefix (str, "visible")) + { + visible = TRUE; + } + else + { + GtkTreeIter child_iter; + gboolean valid; + + /* Recursively check if we have a visible child */ + for (valid = gtk_tree_model_iter_children (model, &child_iter, iter); + valid; valid = gtk_tree_model_iter_next (model, &child_iter)) + { + if (specific_bug_621076_visible_func (model, &child_iter, data)) + { + visible = TRUE; + break; + } + } + } + + if (str) + g_free (str); -int -main (int argc, - char **argv) + return visible; +} + +static void +specific_bug_621076 (void) { - gtk_test_init (&argc, &argv, NULL); + /* Test case for GNOME Bugzilla bug 621076, provided by Xavier Claessens */ + + /* This test case differs from has-child-filter and root-has-child-filter + * in that the visible function both filters on content and model + * structure. Also, it is recursive. + */ + + GtkTreeStore *store; + GtkTreeModel *filter; + GtkWidget *view; + GtkTreeIter group_iter; + GtkTreeIter item_iter; + SignalMonitor *monitor; + + g_test_bug ("621076"); + + store = gtk_tree_store_new (1, G_TYPE_STRING); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), + specific_bug_621076_visible_func, + NULL, NULL); + + view = gtk_tree_view_new_with_model (filter); + g_object_ref_sink (view); + + monitor = signal_monitor_new (filter); + + signal_monitor_append_signal (monitor, ROW_INSERTED, "0"); + gtk_tree_store_insert_with_values (store, &item_iter, NULL, -1, + 0, "visible-group-0", + -1); + signal_monitor_assert_is_empty (monitor); + + /* visible-group-0 is not expanded, so ROW_INSERTED should not be emitted + * for its children. However, ROW_HAS_CHILD_TOGGLED should be emitted on + * visible-group-0 to tell the view that row can be expanded. */ + signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "0"); + signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "0"); + group_iter = item_iter; + gtk_tree_store_insert_with_values (store, &item_iter, &group_iter, -1, + 0, "visible-0:0", + -1); + signal_monitor_assert_is_empty (monitor); + + signal_monitor_append_signal (monitor, ROW_INSERTED, "1"); + gtk_tree_store_insert_with_values (store, &item_iter, NULL, -1, + 0, "visible-group-1", + -1); + signal_monitor_assert_is_empty (monitor); + + /* We are adding an hidden item inside visible-group-1, so + * ROW_HAS_CHILD_TOGGLED should not be emitted. It is emitted though, + * because the signal originating at TreeStore will be propagated, + * as well a generated signal because the state of the parent *could* + * change by a change in the model. + */ + signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "1"); + signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "1"); + group_iter = item_iter; + gtk_tree_store_insert_with_values (store, &item_iter, &group_iter, -1, + 0, "group-1:0", + -1); + signal_monitor_assert_is_empty (monitor); + + /* This group is invisible and its parent too. Nothing should be emitted */ + group_iter = item_iter; + gtk_tree_store_insert_with_values (store, &item_iter, &group_iter, -1, + 0, "group-1:0:0", + -1); + signal_monitor_assert_is_empty (monitor); + + /* Adding a visible item in this group hierarchy will make all nodes + * in this path visible. The first level should simply tell the view + * that it now has a child, and the view will load the tree if needed + * (depends on the expanded state). + */ + signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "1"); + group_iter = item_iter; + gtk_tree_store_insert_with_values (store, &item_iter, &group_iter, -1, + 0, "visible-1:0:0:0", + -1); + signal_monitor_assert_is_empty (monitor); + + check_level_length (GTK_TREE_MODEL_FILTER (filter), "1", 1); + + gtk_tree_store_insert_with_values (store, &item_iter, NULL, -1, + 0, "group-2", + -1); + signal_monitor_assert_is_empty (monitor); + + /* Parent is invisible, and adding this invisible item won't change that, + * so no signal should be emitted. */ + group_iter = item_iter; + gtk_tree_store_insert_with_values (store, NULL, &group_iter, -1, + 0, "invisible-2:0", + -1); + signal_monitor_assert_is_empty (monitor); + + /* This makes group-2 visible, so it gets inserted and tells it has + * children. + */ + signal_monitor_append_signal (monitor, ROW_INSERTED, "2"); + signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "2"); + gtk_tree_store_insert_with_values (store, NULL, &group_iter, -1, + 0, "visible-2:1", + -1); + signal_monitor_assert_is_empty (monitor); + + /* group-2 is already visible, so this time it is a normal insertion */ + gtk_tree_store_insert_with_values (store, NULL, &group_iter, -1, + 0, "visible-2:2", + -1); + signal_monitor_assert_is_empty (monitor); + + + gtk_tree_store_insert_with_values (store, &item_iter, NULL, -1, + 0, "group-3", + -1); + signal_monitor_assert_is_empty (monitor); + + /* Parent is invisible, and adding this invisible item won't change that, + * so no signal should be emitted. */ + group_iter = item_iter; + gtk_tree_store_insert_with_values (store, NULL, &group_iter, -1, + 0, "invisible-3:0", + -1); + signal_monitor_assert_is_empty (monitor); + + gtk_tree_store_insert_with_values (store, &item_iter, &group_iter, -1, + 0, "invisible-3:1", + -1); + signal_monitor_assert_is_empty (monitor); + + /* 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_HAS_CHILD_TOGGLED, "3"); + gtk_tree_store_set (store, &item_iter, 0, "visible-3:1", -1); + signal_monitor_assert_is_empty (monitor); + + /* Make sure all groups are expanded, so the filter has the tree cached */ + gtk_tree_view_expand_all (GTK_TREE_VIEW (view)); + while (gtk_events_pending ()) + gtk_main_iteration (); + + /* Should only yield a row-changed */ + signal_monitor_append_signal (monitor, ROW_CHANGED, "3:0"); + gtk_tree_store_set (store, &item_iter, 0, "visible-3:1", -1); + signal_monitor_assert_is_empty (monitor); + + /* Now remove/hide some items. If a group loses its last item, the group + * should be deleted instead of the item. + */ + + signal_monitor_append_signal (monitor, ROW_DELETED, "2:1"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), &item_iter, "2:2"); + gtk_tree_store_remove (store, &item_iter); + signal_monitor_assert_is_empty (monitor); + + signal_monitor_append_signal (monitor, ROW_DELETED, "2:0"); + signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "2"); + signal_monitor_append_signal (monitor, ROW_DELETED, "2"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), &item_iter, "2:1"); + gtk_tree_store_set (store, &item_iter, 0, "invisible-2:1", -1); + signal_monitor_assert_is_empty (monitor); + + signal_monitor_append_signal (monitor, ROW_DELETED, "1:0:0:0"); + signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "1:0:0"); + signal_monitor_append_signal (monitor, ROW_DELETED, "1:0"); + signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "1"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), &item_iter, "1:0:0:0"); + gtk_tree_store_remove (store, &item_iter); + signal_monitor_assert_is_empty (monitor); + + /* Hide a group using row-changed instead of row-deleted */ + /* Caution: group 2 is gone, so offsets of the signals have moved. */ + signal_monitor_append_signal (monitor, ROW_DELETED, "2:0"); + signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "2"); + signal_monitor_append_signal (monitor, ROW_DELETED, "2"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), &item_iter, + "3:1"); + gtk_tree_store_set (store, &item_iter, 0, "invisible-3:1", -1); + signal_monitor_assert_is_empty (monitor); + +#if 0 + { + GtkWidget *window; + GtkTreeViewColumn *col; + + gtk_tree_view_expand_all (GTK_TREE_VIEW (view)); + + col = gtk_tree_view_column_new_with_attributes ("foo", + gtk_cell_renderer_text_new (), + "text", 0, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (view), col); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_signal_connect (window, "delete-event", + G_CALLBACK (gtk_widget_destroy), NULL); + g_signal_connect (window, "destroy", + G_CALLBACK (gtk_main_quit), NULL); + + gtk_container_add (GTK_CONTAINER (window), view); + + gtk_widget_show (view); + gtk_widget_show (window); + + gtk_main (); + } +#endif + + /* Cleanup */ + signal_monitor_free (monitor); + g_object_unref (view); + g_object_unref (store); + g_object_unref (filter); +} - g_test_add ("/FilterModel/self/verify-test-suite", +/* main */ + +void +register_filter_model_tests (void) +{ + g_test_add ("/TreeModelFilter/self/verify-test-suite", FilterTest, NULL, filter_test_setup, verify_test_suite, filter_test_teardown); - g_test_add ("/FilterModel/self/verify-test-suite/vroot/depth-1", + g_test_add ("/TreeModelFilter/self/verify-test-suite/vroot/depth-1", FilterTest, gtk_tree_path_new_from_indices (2, -1), filter_test_setup, verify_test_suite_vroot, filter_test_teardown); - g_test_add ("/FilterModel/self/verify-test-suite/vroot/depth-2", + g_test_add ("/TreeModelFilter/self/verify-test-suite/vroot/depth-2", FilterTest, gtk_tree_path_new_from_indices (2, 3, -1), filter_test_setup, verify_test_suite_vroot, filter_test_teardown); - g_test_add ("/FilterModel/filled/hide-root-level", + g_test_add ("/TreeModelFilter/filled/hide-root-level", FilterTest, NULL, filter_test_setup, filled_hide_root_level, filter_test_teardown); - g_test_add ("/FilterModel/filled/hide-child-levels", + g_test_add ("/TreeModelFilter/filled/hide-child-levels", FilterTest, NULL, 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 ("/FilterModel/filled/hide-root-level/vroot", + g_test_add ("/TreeModelFilter/filled/hide-root-level/vroot", FilterTest, gtk_tree_path_new_from_indices (2, -1), filter_test_setup, filled_vroot_hide_root_level, filter_test_teardown); - g_test_add ("/FilterModel/filled/hide-child-levels/vroot", + g_test_add ("/TreeModelFilter/filled/hide-child-levels/vroot", FilterTest, gtk_tree_path_new_from_indices (2, -1), 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 ("/FilterModel/empty/show-nodes", + g_test_add ("/TreeModelFilter/empty/show-nodes", FilterTest, NULL, filter_test_setup_empty, empty_show_nodes, filter_test_teardown); - g_test_add ("/FilterModel/empty/show-multiple-nodes", + g_test_add ("/TreeModelFilter/empty/show-multiple-nodes", FilterTest, NULL, filter_test_setup_empty, empty_show_multiple_nodes, filter_test_teardown); - g_test_add ("/FilterModel/empty/show-nodes/vroot", + g_test_add ("/TreeModelFilter/empty/show-nodes/vroot", FilterTest, gtk_tree_path_new_from_indices (2, -1), filter_test_setup_empty, empty_vroot_show_nodes, filter_test_teardown); - g_test_add ("/FilterModel/empty/show-multiple-nodes/vroot", + g_test_add ("/TreeModelFilter/empty/show-multiple-nodes/vroot", FilterTest, gtk_tree_path_new_from_indices (2, -1), filter_test_setup_empty, empty_vroot_show_multiple_nodes, filter_test_teardown); - g_test_add ("/FilterModel/unfiltered/hide-single", + g_test_add ("/TreeModelFilter/unfiltered/hide-single", FilterTest, NULL, filter_test_setup_unfiltered, unfiltered_hide_single, filter_test_teardown); - g_test_add ("/FilterModel/unfiltered/hide-single-child", + 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 ("/FilterModel/unfiltered/hide-single-multi-level", + 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 ("/FilterModel/unfiltered/hide-single/vroot", + g_test_add ("/TreeModelFilter/unfiltered/hide-single/vroot", FilterTest, gtk_tree_path_new_from_indices (2, -1), filter_test_setup_unfiltered, unfiltered_vroot_hide_single, filter_test_teardown); - g_test_add ("/FilterModel/unfiltered/hide-single-child/vroot", + g_test_add ("/TreeModelFilter/unfiltered/hide-single-child/vroot", FilterTest, gtk_tree_path_new_from_indices (2, -1), filter_test_setup_unfiltered, unfiltered_vroot_hide_single_child, filter_test_teardown); - g_test_add ("/FilterModel/unfiltered/hide-single-multi-level/vroot", + 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); - g_test_add ("/FilterModel/unfiltered/show-single", + g_test_add ("/TreeModelFilter/unfiltered/show-single", FilterTest, NULL, filter_test_setup_empty_unfiltered, unfiltered_show_single, filter_test_teardown); - g_test_add ("/FilterModel/unfiltered/show-single-child", + g_test_add ("/TreeModelFilter/unfiltered/show-single-child", FilterTest, NULL, filter_test_setup_empty_unfiltered, unfiltered_show_single_child, filter_test_teardown); - g_test_add ("/FilterModel/unfiltered/show-single-multi-level", + 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 ("/FilterModel/unfiltered/show-single/vroot", + g_test_add ("/TreeModelFilter/unfiltered/show-single/vroot", FilterTest, gtk_tree_path_new_from_indices (2, -1), filter_test_setup_empty_unfiltered, unfiltered_vroot_show_single, filter_test_teardown); - g_test_add ("/FilterModel/unfiltered/show-single-child/vroot", + g_test_add ("/TreeModelFilter/unfiltered/show-single-child/vroot", FilterTest, gtk_tree_path_new_from_indices (2, -1), filter_test_setup_empty_unfiltered, unfiltered_vroot_show_single_child, filter_test_teardown); - g_test_add ("/FilterModel/unfiltered/show-single-multi-level/vroot", + 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_func ("/FilterModel/specific/path-dependent-filter", + 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", + insert_before); + g_test_add_func ("/TreeModelFilter/insert/child", + insert_child); + + /* Removals from child model after creating of filter model */ + g_test_add_func ("/TreeModelFilter/remove/node", + remove_node); + g_test_add_func ("/TreeModelFilter/remove/node-vroot", + remove_node_vroot); + 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/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/reordered", + ref_count_transfer_root_level_reordered); + 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/reordered", + ref_count_transfer_child_level_reordered); + + g_test_add_func ("/TreeModelFilter/specific/path-dependent-filter", specific_path_dependent_filter); - g_test_add_func ("/FilterModel/specific/append-after-collapse", + g_test_add_func ("/TreeModelFilter/specific/append-after-collapse", specific_append_after_collapse); - g_test_add_func ("/FilterModel/specific/sort-filter-remove-node", + g_test_add_func ("/TreeModelFilter/specific/sort-filter-remove-node", specific_sort_filter_remove_node); - g_test_add_func ("/FilterModel/specific/sort-filter-remove-root", + g_test_add_func ("/TreeModelFilter/specific/sort-filter-remove-root", specific_sort_filter_remove_root); - g_test_add_func ("/FilterModel/specific/root-mixed-visibility", + g_test_add_func ("/TreeModelFilter/specific/root-mixed-visibility", specific_root_mixed_visibility); - g_test_add_func ("/FilterModel/specific/has-child-filter", + g_test_add_func ("/TreeModelFilter/specific/has-child-filter", specific_has_child_filter); - g_test_add_func ("/FilterModel/specific/root-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 ("/FilterModel/specific/filter-add-child", + g_test_add_func ("/TreeModelFilter/specific/filter-add-child", specific_filter_add_child); - g_test_add_func ("/FilterModel/specific/list-store-clear", + g_test_add_func ("/TreeModelFilter/specific/list-store-clear", specific_list_store_clear); - - g_test_add_func ("/FilterModel/specific/bug-300089", - specific_bug_300089); - g_test_add_func ("/FilterModel/specific/bug-301558", + g_test_add_func ("/TreeModelFilter/specific/sort-ref-leaf-and-remove-ancestor", + specific_sort_ref_leaf_and_remove_ancestor); + g_test_add_func ("/TreeModelFilter/specific/ref-leaf-and-remove-ancestor", + specific_ref_leaf_and_remove_ancestor); + g_test_add_func ("/TreeModelFilter/specific/virtual-ref-leaf-and-remove-ancestor", + specific_virtual_ref_leaf_and_remove_ancestor); + + g_test_add_func ("/TreeModelFilter/specific/bug-301558", specific_bug_301558); - g_test_add_func ("/FilterModel/specific/bug-311955", + g_test_add_func ("/TreeModelFilter/specific/bug-311955", specific_bug_311955); - g_test_add_func ("/FilterModel/specific/bug-346800", + 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 ("/FilterModel/specific/bug-364946", - specific_bug_364946); - g_test_add_func ("/FilterModel/specific/bug-464173", + g_test_add_func ("/TreeModelFilter/specific/bug-464173", specific_bug_464173); - g_test_add_func ("/FilterModel/specific/bug-540201", + g_test_add_func ("/TreeModelFilter/specific/bug-540201", specific_bug_540201); - g_test_add_func ("/FilterModel/specific/bug-549287", + g_test_add_func ("/TreeModelFilter/specific/bug-549287", specific_bug_549287); - - return g_test_run (); + g_test_add_func ("/TreeModelFilter/specific/bug-621076", + specific_bug_621076); } diff --git a/gtk/tests/gtktreemodelrefcount.c b/gtk/tests/gtktreemodelrefcount.c new file mode 100644 index 0000000000..9c91a587da --- /dev/null +++ b/gtk/tests/gtktreemodelrefcount.c @@ -0,0 +1,286 @@ +/* gtktreemodelrefcount.c + * Copyright (C) 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 Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ + +#include "config.h" +#include "gtktreemodelrefcount.h" + + +/* The purpose of this GtkTreeModel is to keep record of the reference count + * of each node. The reference count does not effect the functioning of + * the model in any way. Because this model is a subclass of GtkTreeStore, + * the GtkTreeStore API should be used to add to and remove nodes from + * this model. We depend on the iter format of GtkTreeStore, which means + * that this model needs to be revised in case the iter format of + * GtkTreeStore is modified. Currently, we make use of the fact that + * the value stored in the user_data field is unique for each node. + */ + +struct _GtkTreeModelRefCountPrivate +{ + GHashTable *node_hash; +}; + +typedef struct +{ + int ref_count; +} +NodeInfo; + + +static void gtk_tree_model_ref_count_tree_model_init (GtkTreeModelIface *iface); +static void gtk_tree_model_ref_count_finalize (GObject *object); + +static NodeInfo *node_info_new (void); +static void node_info_free (NodeInfo *info); + +/* GtkTreeModel interface */ +static void gtk_tree_model_ref_count_ref_node (GtkTreeModel *model, + GtkTreeIter *iter); +static void gtk_tree_model_ref_count_unref_node (GtkTreeModel *model, + GtkTreeIter *iter); + + +G_DEFINE_TYPE_WITH_CODE (GtkTreeModelRefCount, gtk_tree_model_ref_count, GTK_TYPE_TREE_STORE, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, + gtk_tree_model_ref_count_tree_model_init)) + +static void +gtk_tree_model_ref_count_init (GtkTreeModelRefCount *ref_model) +{ + ref_model->priv = G_TYPE_INSTANCE_GET_PRIVATE (ref_model, + GTK_TYPE_TREE_MODEL_REF_COUNT, + GtkTreeModelRefCountPrivate); + + ref_model->priv->node_hash = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify)node_info_free); +} + +static void +gtk_tree_model_ref_count_class_init (GtkTreeModelRefCountClass *ref_model_class) +{ + GObjectClass *object_class; + + object_class = (GObjectClass *) ref_model_class; + + object_class->finalize = gtk_tree_model_ref_count_finalize; + + g_type_class_add_private (object_class, sizeof (GtkTreeModelRefCountPrivate)); +} + +static void +gtk_tree_model_ref_count_tree_model_init (GtkTreeModelIface *iface) +{ + iface->ref_node = gtk_tree_model_ref_count_ref_node; + iface->unref_node = gtk_tree_model_ref_count_unref_node; +} + +static void +gtk_tree_model_ref_count_finalize (GObject *object) +{ + GtkTreeModelRefCount *ref_model = GTK_TREE_MODEL_REF_COUNT (object); + + if (ref_model->priv->node_hash) + { + g_hash_table_destroy (ref_model->priv->node_hash); + ref_model->priv->node_hash = NULL; + } + + G_OBJECT_CLASS (gtk_tree_model_ref_count_parent_class)->finalize (object); +} + + +static NodeInfo * +node_info_new (void) +{ + NodeInfo *info = g_slice_new (NodeInfo); + info->ref_count = 0; + + return info; +} + +static void +node_info_free (NodeInfo *info) +{ + g_slice_free (NodeInfo, info); +} + +static void +gtk_tree_model_ref_count_ref_node (GtkTreeModel *model, + GtkTreeIter *iter) +{ + NodeInfo *info; + GtkTreeModelRefCount *ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + info = g_hash_table_lookup (ref_model->priv->node_hash, iter->user_data); + if (!info) + { + info = node_info_new (); + + g_hash_table_insert (ref_model->priv->node_hash, iter->user_data, info); + } + + info->ref_count++; +} + +static void +gtk_tree_model_ref_count_unref_node (GtkTreeModel *model, + GtkTreeIter *iter) +{ + NodeInfo *info; + GtkTreeModelRefCount *ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + info = g_hash_table_lookup (ref_model->priv->node_hash, iter->user_data); + g_assert (info != NULL); + g_assert (info->ref_count > 0); + + info->ref_count--; +} + + +GtkTreeModel * +gtk_tree_model_ref_count_new (void) +{ + GtkTreeModel *retval; + + retval = g_object_new (gtk_tree_model_ref_count_get_type (), NULL); + + return retval; +} + +static void +dump_iter (GtkTreeModelRefCount *ref_model, + GtkTreeIter *iter) +{ + gchar *path_str; + NodeInfo *info; + GtkTreePath *path; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (ref_model), iter); + path_str = gtk_tree_path_to_string (path); + gtk_tree_path_free (path); + + info = g_hash_table_lookup (ref_model->priv->node_hash, iter->user_data); + if (!info) + g_print ("%-16s ref_count=0\n", path_str); + else + g_print ("%-16s ref_count=%d\n", path_str, info->ref_count); + + g_free (path_str); +} + +static void +gtk_tree_model_ref_count_dump_recurse (GtkTreeModelRefCount *ref_model, + GtkTreeIter *iter) +{ + do + { + GtkTreeIter child; + + dump_iter (ref_model, iter); + + if (gtk_tree_model_iter_children (GTK_TREE_MODEL (ref_model), + &child, iter)) + gtk_tree_model_ref_count_dump_recurse (ref_model, &child); + } + while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ref_model), iter)); +} + +void +gtk_tree_model_ref_count_dump (GtkTreeModelRefCount *ref_model) +{ + GtkTreeIter iter; + + if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ref_model), &iter)) + return; + + gtk_tree_model_ref_count_dump_recurse (ref_model, &iter); +} + +static gboolean +check_iter (GtkTreeModelRefCount *ref_model, + GtkTreeIter *iter, + gint expected_ref_count, + gboolean may_assert) +{ + NodeInfo *info; + + if (may_assert) + g_assert (gtk_tree_store_iter_is_valid (GTK_TREE_STORE (ref_model), iter)); + + info = g_hash_table_lookup (ref_model->priv->node_hash, iter->user_data); + if (!info) + { + if (expected_ref_count == 0) + return TRUE; + else + { + if (may_assert) + g_error ("Expected ref count %d, but node has never been referenced.\n", expected_ref_count); + return FALSE; + } + } + + if (may_assert) + g_assert_cmpint (expected_ref_count, ==, info->ref_count); + + return expected_ref_count == info->ref_count; +} + +gboolean +gtk_tree_model_ref_count_check_level (GtkTreeModelRefCount *ref_model, + GtkTreeIter *parent, + gint expected_ref_count, + gboolean recurse, + gboolean may_assert) +{ + GtkTreeIter iter; + + if (!gtk_tree_model_iter_children (GTK_TREE_MODEL (ref_model), + &iter, parent)) + return TRUE; + + do + { + if (!check_iter (ref_model, &iter, expected_ref_count, may_assert)) + return FALSE; + + if (recurse && + gtk_tree_model_iter_has_child (GTK_TREE_MODEL (ref_model), &iter)) + { + if (!gtk_tree_model_ref_count_check_level (ref_model, &iter, + expected_ref_count, + recurse, may_assert)) + return FALSE; + } + } + while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ref_model), &iter)); + + return TRUE; +} + +gboolean +gtk_tree_model_ref_count_check_node (GtkTreeModelRefCount *ref_model, + GtkTreeIter *iter, + gint expected_ref_count, + gboolean may_assert) +{ + return check_iter (ref_model, iter, expected_ref_count, may_assert); +} diff --git a/gtk/tests/gtktreemodelrefcount.h b/gtk/tests/gtktreemodelrefcount.h new file mode 100644 index 0000000000..f32c8bd2b8 --- /dev/null +++ b/gtk/tests/gtktreemodelrefcount.h @@ -0,0 +1,134 @@ +/* gtktreemodelrefcount.h + * Copyright (C) 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 Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ + +#ifndef __GTK_TREE_MODEL_REF_COUNT_H__ +#define __GTK_TREE_MODEL_REF_COUNT_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_TREE_MODEL_REF_COUNT (gtk_tree_model_ref_count_get_type ()) +#define GTK_TREE_MODEL_REF_COUNT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_MODEL_REF_COUNT, GtkTreeModelRefCount)) +#define GTK_TREE_MODEL_REF_COUNT_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), GTK_TYPE_TREE_MODEL_REF_COUNT, GtkTreeModelRefCountClass)) +#define GTK_IS_TREE_MODEL_REF_COUNT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_MODEL_REF_COUNT)) +#define GTK_IS_TREE_MODEL_REF_COUNT_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), GTK_TYPE_TREE_MODEL_REF_COUNT)) +#define GTK_TREE_MODEL_REF_COUNT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TREE_MODEL_REF_COUNT, GtkTreeModelRefCountClass)) + + +typedef struct _GtkTreeModelRefCount GtkTreeModelRefCount; +typedef struct _GtkTreeModelRefCountClass GtkTreeModelRefCountClass; +typedef struct _GtkTreeModelRefCountPrivate GtkTreeModelRefCountPrivate; + +struct _GtkTreeModelRefCount +{ + GtkTreeStore parent; + + /* < private > */ + GtkTreeModelRefCountPrivate *priv; +}; + +struct _GtkTreeModelRefCountClass +{ + GtkTreeStoreClass parent_class; +}; + + +GType gtk_tree_model_ref_count_get_type (void) G_GNUC_CONST; +GtkTreeModel *gtk_tree_model_ref_count_new (void); + +void gtk_tree_model_ref_count_dump (GtkTreeModelRefCount *ref_model); +gboolean gtk_tree_model_ref_count_check_level (GtkTreeModelRefCount *ref_model, + GtkTreeIter *parent, + gint expected_ref_count, + gboolean recurse, + gboolean may_assert); +gboolean gtk_tree_model_ref_count_check_node (GtkTreeModelRefCount *ref_model, + GtkTreeIter *iter, + gint expected_ref_count, + gboolean may_assert); + +/* A couple of helpers for the tests. Since this model will never be used + * outside of unit tests anyway, it is probably fine to have these here + * without namespacing. + */ + +static inline void +assert_entire_model_unreferenced (GtkTreeModelRefCount *ref_model) +{ + gtk_tree_model_ref_count_check_level (ref_model, NULL, 0, TRUE, TRUE); +} + +static inline void +assert_root_level_unreferenced (GtkTreeModelRefCount *ref_model) +{ + gtk_tree_model_ref_count_check_level (ref_model, NULL, 0, FALSE, TRUE); +} + +static inline void +assert_level_unreferenced (GtkTreeModelRefCount *ref_model, + GtkTreeIter *iter) +{ + gtk_tree_model_ref_count_check_level (ref_model, iter, 0, FALSE, TRUE); +} + +static inline void +assert_entire_model_referenced (GtkTreeModelRefCount *ref_model, + gint ref_count) +{ + gtk_tree_model_ref_count_check_level (ref_model, NULL, ref_count, TRUE, TRUE); +} + +static inline void +assert_not_entire_model_referenced (GtkTreeModelRefCount *ref_model, + gint ref_count) +{ + g_assert_cmpint (gtk_tree_model_ref_count_check_level (ref_model, NULL, + ref_count, + TRUE, FALSE), + ==, FALSE); +} + +static inline void +assert_root_level_referenced (GtkTreeModelRefCount *ref_model, + gint ref_count) +{ + gtk_tree_model_ref_count_check_level (ref_model, NULL, ref_count, + FALSE, TRUE); +} + +static inline void +assert_level_referenced (GtkTreeModelRefCount *ref_model, + gint ref_count, + GtkTreeIter *iter) +{ + gtk_tree_model_ref_count_check_level (ref_model, iter, ref_count, + FALSE, TRUE); +} + +static inline void +assert_node_ref_count (GtkTreeModelRefCount *ref_model, + GtkTreeIter *iter, + gint ref_count) +{ + gtk_tree_model_ref_count_check_node (ref_model, iter, ref_count, TRUE); +} + + +#endif /* __GTK_TREE_MODEL_REF_COUNT_H__ */ diff --git a/gtk/tests/liststore.c b/gtk/tests/liststore.c index e710d9bad2..4efc2879d5 100644 --- a/gtk/tests/liststore.c +++ b/gtk/tests/liststore.c @@ -27,6 +27,8 @@ #include <gtk/gtk.h> +#include "treemodel.h" + static inline gboolean iters_equal (GtkTreeIter *a, GtkTreeIter *b) @@ -998,128 +1000,123 @@ list_store_test_iter_parent_invalid (ListStore *fixture, /* main */ -int -main (int argc, - char **argv) +void +register_list_store_tests (void) { - gtk_test_init (&argc, &argv, NULL); - /* insertion */ - g_test_add_func ("/list-store/insert-high-values", + g_test_add_func ("/ListStore/insert-high-values", list_store_test_insert_high_values); - g_test_add_func ("/list-store/append", + g_test_add_func ("/ListStore/append", list_store_test_append); - g_test_add_func ("/list-store/prepend", + g_test_add_func ("/ListStore/prepend", list_store_test_prepend); - g_test_add_func ("/list-store/insert-after", + g_test_add_func ("/ListStore/insert-after", list_store_test_insert_after); - g_test_add_func ("/list-store/insert-after-NULL", + g_test_add_func ("/ListStore/insert-after-NULL", list_store_test_insert_after_NULL); - g_test_add_func ("/list-store/insert-before", + g_test_add_func ("/ListStore/insert-before", list_store_test_insert_before); - g_test_add_func ("/list-store/insert-before-NULL", + g_test_add_func ("/ListStore/insert-before-NULL", list_store_test_insert_before_NULL); /* setting values (FIXME) */ /* removal */ - g_test_add ("/list-store/remove-begin", ListStore, NULL, + g_test_add ("/ListStore/remove-begin", ListStore, NULL, list_store_setup, list_store_test_remove_begin, list_store_teardown); - g_test_add ("/list-store/remove-middle", ListStore, NULL, + g_test_add ("/ListStore/remove-middle", ListStore, NULL, list_store_setup, list_store_test_remove_middle, list_store_teardown); - g_test_add ("/list-store/remove-end", ListStore, NULL, + g_test_add ("/ListStore/remove-end", ListStore, NULL, list_store_setup, list_store_test_remove_end, list_store_teardown); - g_test_add ("/list-store/clear", ListStore, NULL, + g_test_add ("/ListStore/clear", ListStore, NULL, list_store_setup, list_store_test_clear, list_store_teardown); /* reordering */ - g_test_add ("/list-store/reorder", ListStore, NULL, + g_test_add ("/ListStore/reorder", ListStore, NULL, list_store_setup, list_store_test_reorder, list_store_teardown); /* swapping */ - g_test_add ("/list-store/swap-begin", ListStore, NULL, + g_test_add ("/ListStore/swap-begin", ListStore, NULL, list_store_setup, list_store_test_swap_begin, list_store_teardown); - g_test_add ("/list-store/swap-middle-next", ListStore, NULL, + g_test_add ("/ListStore/swap-middle-next", ListStore, NULL, list_store_setup, list_store_test_swap_middle_next, list_store_teardown); - g_test_add ("/list-store/swap-middle-apart", ListStore, NULL, + g_test_add ("/ListStore/swap-middle-apart", ListStore, NULL, list_store_setup, list_store_test_swap_middle_apart, list_store_teardown); - g_test_add ("/list-store/swap-end", ListStore, NULL, + g_test_add ("/ListStore/swap-end", ListStore, NULL, list_store_setup, list_store_test_swap_end, list_store_teardown); - g_test_add_func ("/list-store/swap-single", + g_test_add_func ("/ListStore/swap-single", list_store_test_swap_single); /* moving */ - g_test_add ("/list-store/move-after-from-start", ListStore, NULL, + g_test_add ("/ListStore/move-after-from-start", ListStore, NULL, list_store_setup, list_store_test_move_after_from_start, list_store_teardown); - g_test_add ("/list-store/move-after-next", ListStore, NULL, + g_test_add ("/ListStore/move-after-next", ListStore, NULL, list_store_setup, list_store_test_move_after_next, list_store_teardown); - g_test_add ("/list-store/move-after-apart", ListStore, NULL, + g_test_add ("/ListStore/move-after-apart", ListStore, NULL, list_store_setup, list_store_test_move_after_apart, list_store_teardown); - g_test_add ("/list-store/move-after-end", ListStore, NULL, + g_test_add ("/ListStore/move-after-end", ListStore, NULL, list_store_setup, list_store_test_move_after_end, list_store_teardown); - g_test_add ("/list-store/move-after-from-end", ListStore, NULL, + g_test_add ("/ListStore/move-after-from-end", ListStore, NULL, list_store_setup, list_store_test_move_after_from_end, list_store_teardown); - g_test_add ("/list-store/move-after-change-ends", ListStore, NULL, + g_test_add ("/ListStore/move-after-change-ends", ListStore, NULL, list_store_setup, list_store_test_move_after_change_ends, list_store_teardown); - g_test_add ("/list-store/move-after-NULL", ListStore, NULL, + g_test_add ("/ListStore/move-after-NULL", ListStore, NULL, list_store_setup, list_store_test_move_after_NULL, list_store_teardown); - g_test_add_func ("/list-store/move-after-single", + g_test_add_func ("/ListStore/move-after-single", list_store_test_move_after_single); - g_test_add ("/list-store/move-before-next", ListStore, NULL, + g_test_add ("/ListStore/move-before-next", ListStore, NULL, list_store_setup, list_store_test_move_before_next, list_store_teardown); - g_test_add ("/list-store/move-before-apart", ListStore, NULL, + g_test_add ("/ListStore/move-before-apart", ListStore, NULL, list_store_setup, list_store_test_move_before_apart, list_store_teardown); - g_test_add ("/list-store/move-before-to-start", ListStore, NULL, + g_test_add ("/ListStore/move-before-to-start", ListStore, NULL, list_store_setup, list_store_test_move_before_to_start, list_store_teardown); - g_test_add ("/list-store/move-before-from-end", ListStore, NULL, + g_test_add ("/ListStore/move-before-from-end", ListStore, NULL, list_store_setup, list_store_test_move_before_from_end, list_store_teardown); - g_test_add ("/list-store/move-before-change-ends", ListStore, NULL, + g_test_add ("/ListStore/move-before-change-ends", ListStore, NULL, list_store_setup, list_store_test_move_before_change_ends, list_store_teardown); - g_test_add ("/list-store/move-before-NULL", ListStore, NULL, + g_test_add ("/ListStore/move-before-NULL", ListStore, NULL, list_store_setup, list_store_test_move_before_NULL, list_store_teardown); - g_test_add_func ("/list-store/move-before-single", + g_test_add_func ("/ListStore/move-before-single", list_store_test_move_before_single); /* iter invalidation */ - g_test_add ("/list-store/iter-prev-invalid", ListStore, NULL, + g_test_add ("/ListStore/iter-prev-invalid", ListStore, NULL, list_store_setup, list_store_test_iter_previous_invalid, list_store_teardown); - g_test_add ("/list-store/iter-next-invalid", ListStore, NULL, + g_test_add ("/ListStore/iter-next-invalid", ListStore, NULL, list_store_setup, list_store_test_iter_next_invalid, list_store_teardown); - g_test_add ("/list-store/iter-children-invalid", ListStore, NULL, + g_test_add ("/ListStore/iter-children-invalid", ListStore, NULL, list_store_setup, list_store_test_iter_children_invalid, list_store_teardown); - g_test_add ("/list-store/iter-nth-child-invalid", ListStore, NULL, + g_test_add ("/ListStore/iter-nth-child-invalid", ListStore, NULL, list_store_setup, list_store_test_iter_nth_child_invalid, list_store_teardown); - g_test_add ("/list-store/iter-parent-invalid", ListStore, NULL, + g_test_add ("/ListStore/iter-parent-invalid", ListStore, NULL, list_store_setup, list_store_test_iter_parent_invalid, list_store_teardown); - - return g_test_run (); } diff --git a/gtk/tests/modelrefcount.c b/gtk/tests/modelrefcount.c new file mode 100644 index 0000000000..c701287927 --- /dev/null +++ b/gtk/tests/modelrefcount.c @@ -0,0 +1,978 @@ +/* GtkTreeModel ref counting tests + * Copyright (C) 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 + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + */ + +#include "gtktreemodelrefcount.h" +#include "treemodel.h" + +/* And the tests themselves */ + +static void +test_list_no_reference (void) +{ + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + + assert_root_level_unreferenced (ref_model); + + g_object_unref (ref_model); +} + +static void +test_list_reference_during_creation (void) +{ + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + tree_view = gtk_tree_view_new_with_model (model); + + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + + assert_root_level_referenced (ref_model, 1); + + gtk_widget_destroy (tree_view); + + assert_root_level_unreferenced (ref_model); + + g_object_unref (ref_model); +} + +static void +test_list_reference_after_creation (void) +{ + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + assert_root_level_unreferenced (ref_model); + + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + + tree_view = gtk_tree_view_new_with_model (model); + + assert_root_level_referenced (ref_model, 1); + + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + + assert_root_level_referenced (ref_model, 1); + + gtk_widget_destroy (tree_view); + + assert_root_level_unreferenced (ref_model); + + g_object_unref (ref_model); +} + +static void +test_list_reference_reordered (void) +{ + GtkTreeIter iter1, iter2, iter3, iter4, iter5; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + assert_root_level_unreferenced (ref_model); + + gtk_tree_store_append (GTK_TREE_STORE (model), &iter1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter3, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter4, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter5, NULL); + + tree_view = gtk_tree_view_new_with_model (model); + + assert_root_level_referenced (ref_model, 1); + + gtk_tree_store_move_after (GTK_TREE_STORE (model), + &iter1, &iter5); + + assert_root_level_referenced (ref_model, 1); + + gtk_tree_store_move_after (GTK_TREE_STORE (model), + &iter3, &iter4); + + assert_root_level_referenced (ref_model, 1); + + gtk_widget_destroy (tree_view); + + assert_root_level_unreferenced (ref_model); + + g_object_unref (ref_model); +} + + +static void +test_tree_no_reference (void) +{ + GtkTreeIter iter, child; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (ref_model); +} + +static void +test_tree_reference_during_creation (void) +{ + GtkTreeIter iter, child; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + tree_view = gtk_tree_view_new_with_model (model); + + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + + assert_root_level_referenced (ref_model, 1); + assert_not_entire_model_referenced (ref_model, 1); + assert_level_unreferenced (ref_model, &child); + + gtk_widget_destroy (tree_view); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (ref_model); +} + +static void +test_tree_reference_after_creation (void) +{ + GtkTreeIter iter, child; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_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, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + + assert_entire_model_unreferenced (ref_model); + + tree_view = gtk_tree_view_new_with_model (model); + + assert_root_level_referenced (ref_model, 1); + assert_not_entire_model_referenced (ref_model, 1); + assert_level_unreferenced (ref_model, &child); + + gtk_widget_destroy (tree_view); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (ref_model); +} + +static void +test_tree_reference_reordered (void) +{ + GtkTreeIter parent; + GtkTreeIter iter1, iter2, iter3, iter4, iter5; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + assert_root_level_unreferenced (ref_model); + + gtk_tree_store_append (GTK_TREE_STORE (model), &parent, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter1, &parent); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter2, &parent); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter3, &parent); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter4, &parent); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter5, &parent); + + tree_view = gtk_tree_view_new_with_model (model); + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + + assert_entire_model_referenced (ref_model, 1); + + gtk_tree_store_move_after (GTK_TREE_STORE (model), + &iter1, &iter5); + + assert_entire_model_referenced (ref_model, 1); + + gtk_tree_store_move_after (GTK_TREE_STORE (model), + &iter3, &iter4); + + assert_entire_model_referenced (ref_model, 1); + + gtk_widget_destroy (tree_view); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (ref_model); +} + +static void +test_tree_reference_expand_all (void) +{ + GtkTreeIter iter, child; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_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, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + + assert_entire_model_unreferenced (ref_model); + + tree_view = gtk_tree_view_new_with_model (model); + + assert_root_level_referenced (ref_model, 1); + assert_not_entire_model_referenced (ref_model, 1); + assert_level_unreferenced (ref_model, &child); + + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + + assert_entire_model_referenced (ref_model, 1); + + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + + assert_root_level_referenced (ref_model, 1); + assert_not_entire_model_referenced (ref_model, 1); + assert_level_unreferenced (ref_model, &child); + + gtk_widget_destroy (tree_view); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (ref_model); +} + +static void +test_tree_reference_collapse_all (void) +{ + GtkTreeIter iter, child; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_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, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter); + + assert_entire_model_unreferenced (ref_model); + + tree_view = gtk_tree_view_new_with_model (model); + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + + assert_entire_model_referenced (ref_model, 1); + + gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view)); + + assert_root_level_referenced (ref_model, 1); + assert_not_entire_model_referenced (ref_model, 1); + assert_level_unreferenced (ref_model, &child); + + gtk_widget_destroy (tree_view); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (ref_model); +} + +static void +test_tree_reference_expand_collapse (void) +{ + GtkTreeIter parent1, parent2, child; + GtkTreePath *path1, *path2; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + tree_view = gtk_tree_view_new_with_model (model); + + gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &parent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &parent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &parent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &child, &parent2); + + path1 = gtk_tree_model_get_path (model, &parent1); + path2 = gtk_tree_model_get_path (model, &parent2); + + assert_level_unreferenced (ref_model, &parent1); + assert_level_unreferenced (ref_model, &parent2); + + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path1, FALSE); + + assert_level_referenced (ref_model, 1, &parent1); + assert_level_unreferenced (ref_model, &parent2); + + gtk_tree_view_collapse_row (GTK_TREE_VIEW (tree_view), path1); + + assert_level_unreferenced (ref_model, &parent1); + assert_level_unreferenced (ref_model, &parent2); + + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path2, FALSE); + + assert_level_unreferenced (ref_model, &parent1); + assert_level_referenced (ref_model, 1, &parent2); + + gtk_tree_view_collapse_row (GTK_TREE_VIEW (tree_view), path2); + + assert_level_unreferenced (ref_model, &parent1); + assert_level_unreferenced (ref_model, &parent2); + + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path2, FALSE); + + assert_level_unreferenced (ref_model, &parent1); + assert_level_referenced (ref_model, 1, &parent2); + + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path1, FALSE); + + assert_level_referenced (ref_model, 1, &parent1); + assert_level_referenced (ref_model, 1, &parent2); + + gtk_tree_path_free (path1); + gtk_tree_path_free (path2); + + gtk_widget_destroy (tree_view); + g_object_unref (ref_model); +} + +static void +test_row_reference_list (void) +{ + GtkTreeIter iter0, iter1, iter2; + GtkTreePath *path; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeRowReference *row_ref; + 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), &iter0, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter2, NULL); + + assert_root_level_unreferenced (ref_model); + + /* create and remove a row ref and check reference counts */ + path = gtk_tree_path_new_from_indices (1, -1); + row_ref = gtk_tree_row_reference_new (model, path); + + assert_node_ref_count (ref_model, &iter0, 0); + assert_node_ref_count (ref_model, &iter1, 1); + assert_node_ref_count (ref_model, &iter2, 0); + + gtk_tree_row_reference_free (row_ref); + + assert_root_level_unreferenced (ref_model); + + /* the same, but then also with a tree view monitoring the model */ + tree_view = gtk_tree_view_new_with_model (model); + + assert_root_level_referenced (ref_model, 1); + + row_ref = gtk_tree_row_reference_new (model, path); + + assert_node_ref_count (ref_model, &iter0, 1); + assert_node_ref_count (ref_model, &iter1, 2); + assert_node_ref_count (ref_model, &iter2, 1); + + gtk_widget_destroy (tree_view); + + assert_node_ref_count (ref_model, &iter0, 0); + assert_node_ref_count (ref_model, &iter1, 1); + assert_node_ref_count (ref_model, &iter2, 0); + + gtk_tree_row_reference_free (row_ref); + + assert_root_level_unreferenced (ref_model); + + gtk_tree_path_free (path); + + g_object_unref (ref_model); +} + +static void +test_row_reference_list_remove (void) +{ + GtkTreeIter iter0, iter1, iter2; + GtkTreePath *path; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeRowReference *row_ref; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_append (GTK_TREE_STORE (model), &iter0, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter2, NULL); + + assert_root_level_unreferenced (ref_model); + + /* test creating the row reference and then removing the node */ + path = gtk_tree_path_new_from_indices (1, -1); + row_ref = gtk_tree_row_reference_new (model, path); + + assert_node_ref_count (ref_model, &iter0, 0); + assert_node_ref_count (ref_model, &iter1, 1); + assert_node_ref_count (ref_model, &iter2, 0); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &iter1); + + assert_root_level_unreferenced (ref_model); + + gtk_tree_row_reference_free (row_ref); + + assert_root_level_unreferenced (ref_model); + + /* test creating a row ref, removing another node and then removing + * the row ref node. + */ + row_ref = gtk_tree_row_reference_new (model, path); + + assert_node_ref_count (ref_model, &iter0, 0); + assert_node_ref_count (ref_model, &iter2, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &iter0); + + assert_root_level_referenced (ref_model, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &iter2); + + g_assert (!gtk_tree_model_get_iter_first (model, &iter0)); + + gtk_tree_row_reference_free (row_ref); + + gtk_tree_path_free (path); + + g_object_unref (ref_model); +} + +static void +test_row_reference_tree (void) +{ + GtkTreeIter iter0, iter1, iter2; + GtkTreeIter child0, child1, child2; + GtkTreeIter grandchild0, grandchild1, grandchild2; + GtkTreePath *path; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeRowReference *row_ref, *row_ref1; + 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), &iter0, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child0, &iter0); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandchild0, &child0); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child1, &iter1); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandchild1, &child1); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child2, &iter2); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandchild2, &child2); + + assert_entire_model_unreferenced (ref_model); + + /* create and remove a row ref and check reference counts */ + path = gtk_tree_path_new_from_indices (1, 0, 0, -1); + row_ref = gtk_tree_row_reference_new (model, path); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &iter0, 0); + assert_node_ref_count (ref_model, &child0, 0); + assert_node_ref_count (ref_model, &grandchild0, 0); + assert_node_ref_count (ref_model, &iter1, 1); + assert_node_ref_count (ref_model, &child1, 1); + assert_node_ref_count (ref_model, &grandchild1, 1); + assert_node_ref_count (ref_model, &iter2, 0); + assert_node_ref_count (ref_model, &child2, 0); + assert_node_ref_count (ref_model, &grandchild2, 0); + + gtk_tree_row_reference_free (row_ref); + + assert_entire_model_unreferenced (ref_model); + + /* again, with path 1:1 */ + path = gtk_tree_path_new_from_indices (1, 0, -1); + row_ref = gtk_tree_row_reference_new (model, path); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &iter0, 0); + assert_node_ref_count (ref_model, &child0, 0); + assert_node_ref_count (ref_model, &grandchild0, 0); + assert_node_ref_count (ref_model, &iter1, 1); + assert_node_ref_count (ref_model, &child1, 1); + assert_node_ref_count (ref_model, &grandchild1, 0); + assert_node_ref_count (ref_model, &iter2, 0); + assert_node_ref_count (ref_model, &child2, 0); + assert_node_ref_count (ref_model, &grandchild2, 0); + + gtk_tree_row_reference_free (row_ref); + + assert_entire_model_unreferenced (ref_model); + + /* both row refs existent at once and also with a tree view monitoring + * the model + */ + tree_view = gtk_tree_view_new_with_model (model); + + assert_root_level_referenced (ref_model, 1); + + path = gtk_tree_path_new_from_indices (1, 0, 0, -1); + row_ref = gtk_tree_row_reference_new (model, path); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &iter0, 1); + assert_node_ref_count (ref_model, &child0, 0); + assert_node_ref_count (ref_model, &grandchild0, 0); + assert_node_ref_count (ref_model, &iter1, 2); + assert_node_ref_count (ref_model, &child1, 1); + assert_node_ref_count (ref_model, &grandchild1, 1); + assert_node_ref_count (ref_model, &iter2, 1); + assert_node_ref_count (ref_model, &child2, 0); + assert_node_ref_count (ref_model, &grandchild2, 0); + + path = gtk_tree_path_new_from_indices (1, 0, -1); + row_ref1 = gtk_tree_row_reference_new (model, path); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &iter0, 1); + assert_node_ref_count (ref_model, &child0, 0); + assert_node_ref_count (ref_model, &grandchild0, 0); + assert_node_ref_count (ref_model, &iter1, 3); + assert_node_ref_count (ref_model, &child1, 2); + assert_node_ref_count (ref_model, &grandchild1, 1); + assert_node_ref_count (ref_model, &iter2, 1); + assert_node_ref_count (ref_model, &child2, 0); + assert_node_ref_count (ref_model, &grandchild2, 0); + + gtk_tree_row_reference_free (row_ref); + + assert_node_ref_count (ref_model, &iter0, 1); + assert_node_ref_count (ref_model, &child0, 0); + assert_node_ref_count (ref_model, &grandchild0, 0); + assert_node_ref_count (ref_model, &iter1, 2); + assert_node_ref_count (ref_model, &child1, 1); + assert_node_ref_count (ref_model, &grandchild1, 0); + assert_node_ref_count (ref_model, &iter2, 1); + assert_node_ref_count (ref_model, &child2, 0); + assert_node_ref_count (ref_model, &grandchild2, 0); + + gtk_widget_destroy (tree_view); + + assert_node_ref_count (ref_model, &iter0, 0); + assert_node_ref_count (ref_model, &child0, 0); + assert_node_ref_count (ref_model, &grandchild0, 0); + assert_node_ref_count (ref_model, &iter1, 1); + assert_node_ref_count (ref_model, &child1, 1); + assert_node_ref_count (ref_model, &grandchild1, 0); + assert_node_ref_count (ref_model, &iter2, 0); + assert_node_ref_count (ref_model, &child2, 0); + assert_node_ref_count (ref_model, &grandchild2, 0); + + gtk_tree_row_reference_free (row_ref1); + + assert_root_level_unreferenced (ref_model); + + g_object_unref (ref_model); +} + +static void +test_row_reference_tree_remove (void) +{ + GtkTreeIter iter0, iter1, iter2; + GtkTreeIter child0, child1, child2; + GtkTreeIter grandchild0, grandchild1, grandchild2; + GtkTreePath *path; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeRowReference *row_ref, *row_ref1, *row_ref2; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_append (GTK_TREE_STORE (model), &iter0, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child0, &iter0); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandchild0, &child0); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child1, &iter1); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandchild1, &child1); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child2, &iter2); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandchild2, &child2); + + assert_entire_model_unreferenced (ref_model); + + path = gtk_tree_path_new_from_indices (1, 0, 0, -1); + row_ref = gtk_tree_row_reference_new (model, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (2, 0, -1); + row_ref1 = gtk_tree_row_reference_new (model, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (2, -1); + row_ref2 = gtk_tree_row_reference_new (model, path); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &iter0, 0); + assert_node_ref_count (ref_model, &child0, 0); + assert_node_ref_count (ref_model, &grandchild0, 0); + assert_node_ref_count (ref_model, &iter1, 1); + assert_node_ref_count (ref_model, &child1, 1); + assert_node_ref_count (ref_model, &grandchild1, 1); + assert_node_ref_count (ref_model, &iter2, 2); + assert_node_ref_count (ref_model, &child2, 1); + assert_node_ref_count (ref_model, &grandchild2, 0); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &grandchild1); + + assert_node_ref_count (ref_model, &iter0, 0); + assert_node_ref_count (ref_model, &child0, 0); + assert_node_ref_count (ref_model, &grandchild0, 0); + assert_node_ref_count (ref_model, &iter1, 0); + assert_node_ref_count (ref_model, &child1, 0); + assert_node_ref_count (ref_model, &iter2, 2); + assert_node_ref_count (ref_model, &child2, 1); + assert_node_ref_count (ref_model, &grandchild2, 0); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &child2); + + assert_node_ref_count (ref_model, &iter0, 0); + assert_node_ref_count (ref_model, &child0, 0); + assert_node_ref_count (ref_model, &grandchild0, 0); + assert_node_ref_count (ref_model, &iter1, 0); + assert_node_ref_count (ref_model, &child1, 0); + assert_node_ref_count (ref_model, &iter2, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &iter2); + + assert_entire_model_unreferenced (ref_model); + + gtk_tree_row_reference_free (row_ref); + gtk_tree_row_reference_free (row_ref1); + gtk_tree_row_reference_free (row_ref2); + + g_object_unref (ref_model); +} + +static void +test_row_reference_tree_remove_ancestor (void) +{ + GtkTreeIter iter0, iter1, iter2; + GtkTreeIter child0, child1, child2; + GtkTreeIter grandchild0, grandchild1, grandchild2; + GtkTreePath *path; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeRowReference *row_ref, *row_ref1; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_append (GTK_TREE_STORE (model), &iter0, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child0, &iter0); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandchild0, &child0); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child1, &iter1); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandchild1, &child1); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child2, &iter2); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandchild2, &child2); + + assert_entire_model_unreferenced (ref_model); + + path = gtk_tree_path_new_from_indices (1, 0, 0, -1); + row_ref = gtk_tree_row_reference_new (model, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (2, 0, -1); + row_ref1 = gtk_tree_row_reference_new (model, path); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &iter0, 0); + assert_node_ref_count (ref_model, &child0, 0); + assert_node_ref_count (ref_model, &grandchild0, 0); + assert_node_ref_count (ref_model, &iter1, 1); + assert_node_ref_count (ref_model, &child1, 1); + assert_node_ref_count (ref_model, &grandchild1, 1); + assert_node_ref_count (ref_model, &iter2, 1); + assert_node_ref_count (ref_model, &child2, 1); + assert_node_ref_count (ref_model, &grandchild2, 0); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &child1); + + assert_node_ref_count (ref_model, &iter0, 0); + assert_node_ref_count (ref_model, &child0, 0); + assert_node_ref_count (ref_model, &grandchild0, 0); + assert_node_ref_count (ref_model, &iter1, 0); + assert_node_ref_count (ref_model, &iter2, 1); + assert_node_ref_count (ref_model, &child2, 1); + assert_node_ref_count (ref_model, &grandchild2, 0); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &iter2); + + assert_entire_model_unreferenced (ref_model); + + gtk_tree_row_reference_free (row_ref); + gtk_tree_row_reference_free (row_ref1); + + g_object_unref (ref_model); +} + +static void +test_row_reference_tree_expand (void) +{ + GtkTreeIter iter0, iter1, iter2; + GtkTreeIter child0, child1, child2; + GtkTreeIter grandchild0, grandchild1, grandchild2; + GtkTreePath *path; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeRowReference *row_ref, *row_ref1, *row_ref2; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + tree_view = gtk_tree_view_new_with_model (model); + + gtk_tree_store_append (GTK_TREE_STORE (model), &iter0, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child0, &iter0); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandchild0, &child0); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child1, &iter1); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandchild1, &child1); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &child2, &iter2); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandchild2, &child2); + + assert_root_level_referenced (ref_model, 1); + + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + + assert_entire_model_referenced (ref_model, 1); + + path = gtk_tree_path_new_from_indices (1, 0, 0, -1); + row_ref = gtk_tree_row_reference_new (model, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (2, 0, -1); + row_ref1 = gtk_tree_row_reference_new (model, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (2, -1); + row_ref2 = gtk_tree_row_reference_new (model, path); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &iter0, 1); + assert_node_ref_count (ref_model, &child0, 1); + assert_node_ref_count (ref_model, &grandchild0, 1); + assert_node_ref_count (ref_model, &iter1, 2); + assert_node_ref_count (ref_model, &child1, 2); + assert_node_ref_count (ref_model, &grandchild1, 2); + assert_node_ref_count (ref_model, &iter2, 3); + assert_node_ref_count (ref_model, &child2, 2); + assert_node_ref_count (ref_model, &grandchild2, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &grandchild1); + + assert_node_ref_count (ref_model, &iter0, 1); + assert_node_ref_count (ref_model, &child0, 1); + assert_node_ref_count (ref_model, &grandchild0, 1); + assert_node_ref_count (ref_model, &iter1, 1); + assert_node_ref_count (ref_model, &child1, 1); + assert_node_ref_count (ref_model, &iter2, 3); + assert_node_ref_count (ref_model, &child2, 2); + assert_node_ref_count (ref_model, &grandchild2, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &child2); + + assert_node_ref_count (ref_model, &iter0, 1); + assert_node_ref_count (ref_model, &child0, 1); + assert_node_ref_count (ref_model, &grandchild0, 1); + assert_node_ref_count (ref_model, &iter1, 1); + assert_node_ref_count (ref_model, &child1, 1); + assert_node_ref_count (ref_model, &iter2, 2); + + gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view)); + + assert_node_ref_count (ref_model, &iter0, 1); + assert_node_ref_count (ref_model, &child0, 0); + assert_node_ref_count (ref_model, &grandchild0, 0); + assert_node_ref_count (ref_model, &iter1, 1); + assert_node_ref_count (ref_model, &child1, 0); + assert_node_ref_count (ref_model, &iter2, 2); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &iter2); + + assert_node_ref_count (ref_model, &iter0, 1); + assert_node_ref_count (ref_model, &child0, 0); + assert_node_ref_count (ref_model, &grandchild0, 0); + assert_node_ref_count (ref_model, &iter1, 1); + assert_node_ref_count (ref_model, &child1, 0); + + gtk_tree_row_reference_free (row_ref); + gtk_tree_row_reference_free (row_ref1); + gtk_tree_row_reference_free (row_ref2); + + gtk_widget_destroy (tree_view); + g_object_unref (ref_model); +} + +void +register_model_ref_count_tests (void) +{ + /* lists (though based on GtkTreeStore) */ + g_test_add_func ("/TreeModel/ref-count/list/no-reference", + test_list_no_reference); + g_test_add_func ("/TreeModel/ref-count/list/reference-during-creation", + test_list_reference_during_creation); + g_test_add_func ("/TreeModel/ref-count/list/reference-after-creation", + test_list_reference_after_creation); + g_test_add_func ("/TreeModel/ref-count/list/reference-reordered", + test_list_reference_reordered); + + /* trees */ + g_test_add_func ("/TreeModel/ref-count/tree/no-reference", + test_tree_no_reference); + g_test_add_func ("/TreeModel/ref-count/tree/reference-during-creation", + test_tree_reference_during_creation); + g_test_add_func ("/TreeModel/ref-count/tree/reference-after-creation", + test_tree_reference_after_creation); + g_test_add_func ("/TreeModel/ref-count/tree/expand-all", + test_tree_reference_expand_all); + g_test_add_func ("/TreeModel/ref-count/tree/collapse-all", + test_tree_reference_collapse_all); + g_test_add_func ("/TreeModel/ref-count/tree/expand-collapse", + test_tree_reference_expand_collapse); + g_test_add_func ("/TreeModel/ref-count/tree/reference-reordered", + test_tree_reference_reordered); + + /* row references */ + g_test_add_func ("/TreeModel/ref-count/row-reference/list", + test_row_reference_list); + g_test_add_func ("/TreeModel/ref-count/row-reference/list-remove", + test_row_reference_list_remove); + g_test_add_func ("/TreeModel/ref-count/row-reference/tree", + test_row_reference_tree); + g_test_add_func ("/TreeModel/ref-count/row-reference/tree-remove", + test_row_reference_tree_remove); + g_test_add_func ("/TreeModel/ref-count/row-reference/tree-remove-ancestor", + test_row_reference_tree_remove_ancestor); + g_test_add_func ("/TreeModel/ref-count/row-reference/tree-expand", + test_row_reference_tree_expand); +} diff --git a/gtk/tests/sortmodel.c b/gtk/tests/sortmodel.c new file mode 100644 index 0000000000..a58359d6d1 --- /dev/null +++ b/gtk/tests/sortmodel.c @@ -0,0 +1,1151 @@ +/* Extensive GtkTreeModelSort tests. + * 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 + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + */ + +#include <gtk/gtk.h> + +#include "treemodel.h" +#include "gtktreemodelrefcount.h" + + +static void +ref_count_single_level (void) +{ + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *sort_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, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + + assert_root_level_unreferenced (ref_model); + + sort_model = gtk_tree_model_sort_new_with_model (model); + tree_view = gtk_tree_view_new_with_model (sort_model); + + assert_entire_model_referenced (ref_model, 1); + + gtk_widget_destroy (tree_view); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (sort_model); + g_object_unref (ref_model); +} + +static void +ref_count_two_levels (void) +{ + GtkTreeIter parent1, parent2, iter; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *sort_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, &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); + + sort_model = gtk_tree_model_sort_new_with_model (model); + tree_view = gtk_tree_view_new_with_model (sort_model); + + assert_root_level_referenced (ref_model, 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, 1); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter, 1); + + gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view)); + + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter, 0); + + gtk_tree_model_sort_clear_cache (GTK_TREE_MODEL_SORT (sort_model)); + + assert_root_level_referenced (ref_model, 1); + assert_node_ref_count (ref_model, &iter, 0); + + gtk_widget_destroy (tree_view); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (sort_model); + g_object_unref (ref_model); +} + +static void +ref_count_three_levels (void) +{ + GtkTreeIter grandparent1, grandparent2, parent1, parent2; + GtkTreeIter iter_parent1, iter_parent2; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *sort_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 + * + 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, &parent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2, &parent2); + + assert_entire_model_unreferenced (ref_model); + + sort_model = gtk_tree_model_sort_new_with_model (model); + tree_view = gtk_tree_view_new_with_model (sort_model); + + assert_root_level_referenced (ref_model, 1); + assert_node_ref_count (ref_model, &parent1, 0); + 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, 1); + 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, 0); + + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, TRUE); + + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 2); + 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, 1); + + gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view)); + + assert_node_ref_count (ref_model, &grandparent1, 1); + 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, 0); + + gtk_tree_model_sort_clear_cache (GTK_TREE_MODEL_SORT (sort_model)); + + assert_root_level_referenced (ref_model, 1); + assert_node_ref_count (ref_model, &parent1, 0); + assert_node_ref_count (ref_model, &parent2, 0); + + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE); + + assert_node_ref_count (ref_model, &grandparent1, 1); + 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, 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, 1); + assert_node_ref_count (ref_model, &grandparent2, 2); + 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, 1); + + gtk_tree_view_collapse_row (GTK_TREE_VIEW (tree_view), path); + + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 2); + 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, 0); + + gtk_tree_model_sort_clear_cache (GTK_TREE_MODEL_SORT (sort_model)); + + assert_node_ref_count (ref_model, &grandparent1, 1); + 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, 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, 1); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 0); + 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, 0); + + gtk_tree_model_sort_clear_cache (GTK_TREE_MODEL_SORT (sort_model)); + + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &parent1, 0); + 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, 0); + + gtk_widget_destroy (tree_view); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (sort_model); + g_object_unref (ref_model); +} + +static void +ref_count_delete_row (void) +{ + GtkTreeIter grandparent1, grandparent2, parent1, parent2; + GtkTreeIter iter_parent1, iter_parent2; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *sort_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 + * + 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, &parent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2, &parent2); + + assert_entire_model_unreferenced (ref_model); + + sort_model = gtk_tree_model_sort_new_with_model (model); + tree_view = gtk_tree_view_new_with_model (sort_model); + + assert_root_level_referenced (ref_model, 1); + assert_node_ref_count (ref_model, &parent1, 0); + 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, 1); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 2); + 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, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &iter_parent2); + + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 2); + assert_level_referenced (ref_model, 1, &parent1); + assert_node_ref_count (ref_model, &parent2, 2); + assert_level_referenced (ref_model, 1, &parent2); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &parent1); + + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent2, 2); + assert_level_referenced (ref_model, 1, &parent2); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent2); + + assert_node_ref_count (ref_model, &grandparent1, 1); + + gtk_tree_model_sort_clear_cache (GTK_TREE_MODEL_SORT (sort_model)); + + assert_node_ref_count (ref_model, &grandparent1, 1); + + gtk_widget_destroy (tree_view); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (sort_model); + g_object_unref (ref_model); +} + +static void +ref_count_cleanup (void) +{ + GtkTreeIter grandparent1, grandparent2, parent1, parent2; + GtkTreeIter iter_parent1, iter_parent2; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *sort_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 + * + 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, &parent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2, &parent2); + + sort_model = gtk_tree_model_sort_new_with_model (model); + tree_view = gtk_tree_view_new_with_model (sort_model); + + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + + gtk_widget_destroy (tree_view); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 1); + 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, 0); + + gtk_tree_model_sort_clear_cache (GTK_TREE_MODEL_SORT (sort_model)); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (sort_model); + g_object_unref (ref_model); +} + +static void +ref_count_row_ref (void) +{ + GtkTreeIter grandparent1, grandparent2, parent1, parent2; + GtkTreeIter iter_parent1, iter_parent2; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *sort_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, &parent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2, &parent2); + + sort_model = gtk_tree_model_sort_new_with_model (model); + tree_view = gtk_tree_view_new_with_model (sort_model); + + path = gtk_tree_path_new_from_indices (1, 1, 1, -1); + row_ref = gtk_tree_row_reference_new (sort_model, path); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &grandparent1, 1); + /* Referenced because the node is visible, its child level is built + * and referenced by the row ref. + */ + assert_node_ref_count (ref_model, &grandparent2, 3); + assert_node_ref_count (ref_model, &parent1, 0); + /* Referenced by the row ref and because its child level is built. */ + 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, 1); + + gtk_tree_row_reference_free (row_ref); + + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 0); + 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, 0); + + path = gtk_tree_path_new_from_indices (1, 1, 1, -1); + row_ref = gtk_tree_row_reference_new (sort_model, path); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &grandparent1, 1); + /* Referenced because the node is visible, its child level is built + * and referenced by the row ref. + */ + assert_node_ref_count (ref_model, &grandparent2, 3); + assert_node_ref_count (ref_model, &parent1, 0); + /* Referenced by the row ref and because its child level is built. */ + 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, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &parent2); + + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &parent1, 0); + assert_node_ref_count (ref_model, &iter_parent1, 0); + + gtk_tree_row_reference_free (row_ref); + + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &parent1, 0); + assert_node_ref_count (ref_model, &iter_parent1, 0); + + gtk_widget_destroy (tree_view); + g_object_unref (sort_model); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (ref_model); +} + +static void +ref_count_reorder_single (void) +{ + GtkTreeIter iter1, iter2, iter3, iter4, iter5; + GtkTreeIter siter1, siter2, siter3, siter4, siter5; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *sort_model; + GtkWidget *tree_view; + GType column_types[] = { G_TYPE_INT }; + + 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_insert_with_values (GTK_TREE_STORE (model), &iter1, NULL, 0, + 0, 30, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter2, NULL, 1, + 0, 40, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter3, NULL, 2, + 0, 10, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter4, NULL, 3, + 0, 20, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter5, NULL, 4, + 0, 60, -1); + + assert_root_level_unreferenced (ref_model); + + sort_model = gtk_tree_model_sort_new_with_model (model); + tree_view = gtk_tree_view_new_with_model (sort_model); + + assert_entire_model_referenced (ref_model, 1); + + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter1, &iter1); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter2, &iter2); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter3, &iter3); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter4, &iter4); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter5, &iter5); + + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &siter1); + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &siter1); + + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &siter3); + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &siter3); + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &siter3); + + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &siter5); + + assert_node_ref_count (ref_model, &iter1, 3); + assert_node_ref_count (ref_model, &iter2, 1); + assert_node_ref_count (ref_model, &iter3, 4); + assert_node_ref_count (ref_model, &iter4, 1); + assert_node_ref_count (ref_model, &iter5, 2); + + /* Sort */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + 0, GTK_SORT_ASCENDING); + + assert_node_ref_count (ref_model, &iter1, 3); + assert_node_ref_count (ref_model, &iter2, 1); + assert_node_ref_count (ref_model, &iter3, 4); + assert_node_ref_count (ref_model, &iter4, 1); + assert_node_ref_count (ref_model, &iter5, 2); + + /* Re-translate the iters after sorting */ + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter1, &iter1); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter2, &iter2); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter3, &iter3); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter4, &iter4); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter5, &iter5); + + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &siter1); + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &siter1); + + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &siter3); + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &siter3); + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &siter3); + + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &siter5); + + assert_entire_model_referenced (ref_model, 1); + + gtk_widget_destroy (tree_view); + g_object_unref (sort_model); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (ref_model); +} + +static void +ref_count_reorder_two (void) +{ + GtkTreeIter iter1, iter2, iter3, iter4, iter5; + GtkTreeIter citer1, citer2, citer3, citer4, citer5; + GtkTreeIter siter1, siter2, siter3, siter4, siter5; + GtkTreeIter sciter1, sciter2, sciter3, sciter4, sciter5; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *sort_model; + GtkWidget *tree_view; + GType column_types[] = { G_TYPE_INT }; + + 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_insert_with_values (GTK_TREE_STORE (model), &iter1, NULL, 0, + 0, 30, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter2, NULL, 1, + 0, 40, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter3, NULL, 2, + 0, 10, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter4, NULL, 3, + 0, 20, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter5, NULL, 4, + 0, 60, -1); + + /* Child level */ + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &citer1, &iter1, 0, + 0, 30, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &citer2, &iter1, 1, + 0, 40, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &citer3, &iter1, 2, + 0, 10, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &citer4, &iter1, 3, + 0, 20, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &citer5, &iter1, 4, + 0, 60, -1); + + assert_root_level_unreferenced (ref_model); + + sort_model = gtk_tree_model_sort_new_with_model (model); + tree_view = gtk_tree_view_new_with_model (sort_model); + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + + assert_node_ref_count (ref_model, &iter1, 2); + assert_node_ref_count (ref_model, &iter2, 1); + assert_node_ref_count (ref_model, &iter3, 1); + assert_node_ref_count (ref_model, &iter4, 1); + assert_node_ref_count (ref_model, &iter5, 1); + + assert_level_referenced (ref_model, 1, &iter1); + + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter1, &iter1); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter2, &iter2); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter3, &iter3); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter4, &iter4); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter5, &iter5); + + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &sciter1, &citer1); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &sciter2, &citer2); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &sciter3, &citer3); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &sciter4, &citer4); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &sciter5, &citer5); + + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &siter1); + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &siter1); + + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &siter3); + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &siter3); + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &siter3); + + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &siter5); + + assert_node_ref_count (ref_model, &iter1, 4); + assert_node_ref_count (ref_model, &iter2, 1); + assert_node_ref_count (ref_model, &iter3, 4); + assert_node_ref_count (ref_model, &iter4, 1); + assert_node_ref_count (ref_model, &iter5, 2); + + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &sciter3); + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &sciter3); + + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &sciter5); + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &sciter5); + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &sciter5); + + gtk_tree_model_ref_node (GTK_TREE_MODEL (sort_model), &sciter1); + + assert_node_ref_count (ref_model, &citer1, 2); + assert_node_ref_count (ref_model, &citer2, 1); + assert_node_ref_count (ref_model, &citer3, 3); + assert_node_ref_count (ref_model, &citer4, 1); + assert_node_ref_count (ref_model, &citer5, 4); + + /* Sort */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + 0, GTK_SORT_ASCENDING); + + assert_node_ref_count (ref_model, &iter1, 4); + assert_node_ref_count (ref_model, &iter2, 1); + assert_node_ref_count (ref_model, &iter3, 4); + assert_node_ref_count (ref_model, &iter4, 1); + assert_node_ref_count (ref_model, &iter5, 2); + + assert_node_ref_count (ref_model, &citer1, 2); + assert_node_ref_count (ref_model, &citer2, 1); + assert_node_ref_count (ref_model, &citer3, 3); + assert_node_ref_count (ref_model, &citer4, 1); + assert_node_ref_count (ref_model, &citer5, 4); + + /* Re-translate the iters after sorting */ + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter1, &iter1); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter2, &iter2); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter3, &iter3); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter4, &iter4); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &siter5, &iter5); + + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &sciter1, &citer1); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &sciter2, &citer2); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &sciter3, &citer3); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &sciter4, &citer4); + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &sciter5, &citer5); + + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &siter1); + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &siter1); + + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &siter3); + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &siter3); + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &siter3); + + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &siter5); + + assert_node_ref_count (ref_model, &iter1, 2); + assert_node_ref_count (ref_model, &iter2, 1); + assert_node_ref_count (ref_model, &iter3, 1); + assert_node_ref_count (ref_model, &iter4, 1); + assert_node_ref_count (ref_model, &iter5, 1); + + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &sciter3); + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &sciter3); + + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &sciter5); + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &sciter5); + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &sciter5); + + gtk_tree_model_unref_node (GTK_TREE_MODEL (sort_model), &sciter1); + + assert_level_referenced (ref_model, 1, &iter1); + + gtk_widget_destroy (tree_view); + g_object_unref (sort_model); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (ref_model); +} + +static void +check_sort_order (GtkTreeModel *sort_model, + GtkSortType sort_order, + const char *parent_path) +{ + int prev_value; + GtkTreeIter siter; + + if (!parent_path) + gtk_tree_model_get_iter_first (sort_model, &siter); + else + { + GtkTreePath *path; + + path = gtk_tree_path_new_from_string (parent_path); + gtk_tree_path_append_index (path, 0); + + gtk_tree_model_get_iter (sort_model, &siter, path); + + gtk_tree_path_free (path); + } + + if (sort_order == GTK_SORT_ASCENDING) + prev_value = -1; + else + prev_value = INT_MAX; + + do + { + int value; + + gtk_tree_model_get (sort_model, &siter, 0, &value, -1); + if (sort_order == GTK_SORT_ASCENDING) + g_assert (prev_value <= value); + else + g_assert (prev_value >= value); + + prev_value = value; + } + while (gtk_tree_model_iter_next (sort_model, &siter)); +} + +static void +rows_reordered_single_level (void) +{ + GtkTreeIter iter1, iter2, iter3, iter4, iter5; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *sort_model; + GtkWidget *tree_view; + SignalMonitor *monitor; + GtkTreePath *path; + GType column_types[] = { G_TYPE_INT }; + int order[][5] = + { + { 2, 3, 0, 1, 4 }, + { 4, 3, 2, 1, 0 }, + { 2, 1, 4, 3, 0 } + }; + + 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_insert_with_values (GTK_TREE_STORE (model), &iter1, NULL, 0, + 0, 30, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter2, NULL, 1, + 0, 40, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter3, NULL, 2, + 0, 10, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter4, NULL, 3, + 0, 20, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter5, NULL, 4, + 0, 60, -1); + + sort_model = gtk_tree_model_sort_new_with_model (model); + tree_view = gtk_tree_view_new_with_model (sort_model); + + monitor = signal_monitor_new (sort_model); + + /* Sort */ + path = gtk_tree_path_new (); + signal_monitor_append_signal_reordered (monitor, + ROWS_REORDERED, + path, order[0], 5); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + 0, GTK_SORT_ASCENDING); + signal_monitor_assert_is_empty (monitor); + check_sort_order (sort_model, GTK_SORT_ASCENDING, NULL); + + signal_monitor_append_signal_reordered (monitor, + ROWS_REORDERED, + path, order[1], 5); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + 0, GTK_SORT_DESCENDING); + signal_monitor_assert_is_empty (monitor); + check_sort_order (sort_model, GTK_SORT_DESCENDING, NULL); + + signal_monitor_append_signal_reordered (monitor, + ROWS_REORDERED, + path, order[2], 5); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + signal_monitor_assert_is_empty (monitor); + + gtk_tree_path_free (path); + signal_monitor_free (monitor); + + gtk_widget_destroy (tree_view); + g_object_unref (sort_model); + + assert_entire_model_unreferenced (ref_model); + + g_object_unref (ref_model); +} + +static void +rows_reordered_two_levels (void) +{ + GtkTreeIter iter1, iter2, iter3, iter4, iter5; + GtkTreeIter citer1, citer2, citer3, citer4, citer5; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *sort_model; + GtkWidget *tree_view; + SignalMonitor *monitor; + GtkTreePath *path, *child_path; + GType column_types[] = { G_TYPE_INT }; + int order[][5] = + { + { 2, 3, 0, 1, 4 }, + { 4, 3, 2, 1, 0 }, + { 2, 1, 4, 3, 0 } + }; + + 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_insert_with_values (GTK_TREE_STORE (model), &iter1, NULL, 0, + 0, 30, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter2, NULL, 1, + 0, 40, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter3, NULL, 2, + 0, 10, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter4, NULL, 3, + 0, 20, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter5, NULL, 4, + 0, 60, -1); + + /* Child level */ + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &citer1, &iter1, 0, + 0, 30, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &citer2, &iter1, 1, + 0, 40, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &citer3, &iter1, 2, + 0, 10, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &citer4, &iter1, 3, + 0, 20, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &citer5, &iter1, 4, + 0, 60, -1); + + sort_model = gtk_tree_model_sort_new_with_model (model); + tree_view = gtk_tree_view_new_with_model (sort_model); + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + + monitor = signal_monitor_new (sort_model); + + /* Sort */ + path = gtk_tree_path_new (); + child_path = gtk_tree_path_new_from_indices (2, -1); + signal_monitor_append_signal_reordered (monitor, + ROWS_REORDERED, + path, order[0], 5); + signal_monitor_append_signal_reordered (monitor, + ROWS_REORDERED, + child_path, order[0], 5); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + 0, GTK_SORT_ASCENDING); + signal_monitor_assert_is_empty (monitor); + check_sort_order (sort_model, GTK_SORT_ASCENDING, NULL); + /* The parent node of the child level moved due to sorting */ + check_sort_order (sort_model, GTK_SORT_ASCENDING, "2"); + + signal_monitor_append_signal_reordered (monitor, + ROWS_REORDERED, + path, order[1], 5); + signal_monitor_append_signal_reordered (monitor, + ROWS_REORDERED, + child_path, order[1], 5); + gtk_tree_path_free (child_path); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + 0, GTK_SORT_DESCENDING); + signal_monitor_assert_is_empty (monitor); + check_sort_order (sort_model, GTK_SORT_DESCENDING, NULL); + /* The parent node of the child level moved due to sorting */ + check_sort_order (sort_model, GTK_SORT_DESCENDING, "2"); + + child_path = gtk_tree_path_new_from_indices (0, -1); + signal_monitor_append_signal_reordered (monitor, + ROWS_REORDERED, + path, order[2], 5); + signal_monitor_append_signal_reordered (monitor, + ROWS_REORDERED, + child_path, order[2], 5); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + signal_monitor_assert_is_empty (monitor); + + gtk_tree_path_free (path); + gtk_tree_path_free (child_path); + signal_monitor_free (monitor); + + gtk_widget_destroy (tree_view); + g_object_unref (sort_model); + + g_object_unref (ref_model); +} + +static void +sorted_insert (void) +{ + GtkTreeIter iter1, iter2, iter3, iter4, iter5, new_iter; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *sort_model; + GtkWidget *tree_view; + SignalMonitor *monitor; + GtkTreePath *path; + GType column_types[] = { G_TYPE_INT }; + int order0[] = { 1, 2, 3, 0, 4, 5, 6 }; + + 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_insert_with_values (GTK_TREE_STORE (model), &iter1, NULL, 0, + 0, 30, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter2, NULL, 1, + 0, 40, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter3, NULL, 2, + 0, 10, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter4, NULL, 3, + 0, 20, -1); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &iter5, NULL, 4, + 0, 60, -1); + + sort_model = gtk_tree_model_sort_new_with_model (model); + tree_view = gtk_tree_view_new_with_model (sort_model); + + /* Sort */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + 0, GTK_SORT_ASCENDING); + check_sort_order (sort_model, GTK_SORT_ASCENDING, NULL); + + monitor = signal_monitor_new (sort_model); + + /* Insert a new item */ + signal_monitor_append_signal (monitor, ROW_INSERTED, "4"); + gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &new_iter, NULL, + 5, 0, 50, -1); + signal_monitor_assert_is_empty (monitor); + check_sort_order (sort_model, GTK_SORT_ASCENDING, NULL); + + /* Sort the tree sort and append a new item */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), + 0, GTK_SORT_ASCENDING); + check_sort_order (model, GTK_SORT_ASCENDING, NULL); + + path = gtk_tree_path_new (); + signal_monitor_append_signal (monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal_reordered (monitor, + ROWS_REORDERED, + path, order0, 7); + signal_monitor_append_signal (monitor, ROW_CHANGED, "3"); + gtk_tree_store_append (GTK_TREE_STORE (model), &new_iter, NULL); + gtk_tree_store_set (GTK_TREE_STORE (model), &new_iter, 0, 35, -1); + check_sort_order (model, GTK_SORT_ASCENDING, NULL); + check_sort_order (sort_model, GTK_SORT_ASCENDING, NULL); + + gtk_tree_path_free (path); + signal_monitor_free (monitor); + + gtk_widget_destroy (tree_view); + g_object_unref (sort_model); + + g_object_unref (ref_model); +} + + +static void +specific_bug_300089 (void) +{ + /* Test case for GNOME Bugzilla bug 300089. Written by + * Matthias Clasen. + */ + GtkTreeModel *sort_model, *child_model; + GtkTreePath *path; + GtkTreeIter iter, iter2, sort_iter; + + g_test_bug ("300089"); + + child_model = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_STRING)); + + gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL); + gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "A", -1); + gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL); + gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "B", -1); + + gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter2, &iter); + gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter2, 0, "D", -1); + gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter2, &iter); + gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter2, 0, "E", -1); + + gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL); + gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "C", -1); + + + sort_model = GTK_TREE_MODEL (gtk_tree_model_sort_new_with_model (child_model)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + 0, GTK_SORT_ASCENDING); + + path = gtk_tree_path_new_from_indices (1, 1, -1); + + /* make sure a level is constructed */ + gtk_tree_model_get_iter (sort_model, &sort_iter, path); + + /* change the "E" row in a way that causes it to change position */ + gtk_tree_model_get_iter (child_model, &iter, path); + gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "A", -1); + + gtk_tree_path_free (path); +} + +static void +specific_bug_364946 (void) +{ + /* This is a test case for GNOME Bugzilla bug 364946. It was written + * by Andreas Koehler. + */ + GtkTreeStore *store; + GtkTreeIter a, aa, aaa, aab, iter; + GtkTreeModel *s_model; + + g_test_bug ("364946"); + + store = gtk_tree_store_new (1, G_TYPE_STRING); + + gtk_tree_store_append (store, &a, NULL); + gtk_tree_store_set (store, &a, 0, "0", -1); + + gtk_tree_store_append (store, &aa, &a); + gtk_tree_store_set (store, &aa, 0, "0:0", -1); + + gtk_tree_store_append (store, &aaa, &aa); + gtk_tree_store_set (store, &aaa, 0, "0:0:0", -1); + + gtk_tree_store_append (store, &aab, &aa); + gtk_tree_store_set (store, &aab, 0, "0:0:1", -1); + + s_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (s_model), 0, + GTK_SORT_ASCENDING); + + gtk_tree_model_get_iter_from_string (s_model, &iter, "0:0:0"); + + gtk_tree_store_set (store, &aaa, 0, "0:0:0", -1); + gtk_tree_store_remove (store, &aaa); + gtk_tree_store_remove (store, &aab); + + gtk_tree_model_sort_clear_cache (GTK_TREE_MODEL_SORT (s_model)); +} + +/* main */ + +void +register_sort_model_tests (void) +{ + g_test_add_func ("/TreeModelSort/ref-count/single-level", + ref_count_single_level); + g_test_add_func ("/TreeModelSort/ref-count/two-levels", + ref_count_two_levels); + g_test_add_func ("/TreeModelSort/ref-count/three-levels", + ref_count_three_levels); + g_test_add_func ("/TreeModelSort/ref-count/delete-row", + ref_count_delete_row); + g_test_add_func ("/TreeModelSort/ref-count/cleanup", + ref_count_cleanup); + g_test_add_func ("/TreeModelSort/ref-count/row-ref", + ref_count_row_ref); + g_test_add_func ("/TreeModelSort/ref-count/reorder/single-level", + ref_count_reorder_single); + g_test_add_func ("/TreeModelSort/ref-count/reorder/two-levels", + ref_count_reorder_two); + + g_test_add_func ("/TreeModelSort/rows-reordered/single-level", + rows_reordered_single_level); + g_test_add_func ("/TreeModelSort/rows-reordered/two-levels", + rows_reordered_two_levels); + g_test_add_func ("/TreeModelSort/sorted-insert", + sorted_insert); + + g_test_add_func ("/TreeModelSort/specific/bug-300089", + specific_bug_300089); + g_test_add_func ("/TreeModelSort/specific/bug-364946", + specific_bug_364946); +} diff --git a/gtk/tests/treemodel.c b/gtk/tests/treemodel.c new file mode 100644 index 0000000000..97bbc14b27 --- /dev/null +++ b/gtk/tests/treemodel.c @@ -0,0 +1,347 @@ +/* Main wrapper for TreeModel test suite. + * Copyright (C) 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 + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + */ + +#include <gtk/gtk.h> + +#include "treemodel.h" + +int +main (int argc, + char **argv) +{ + gtk_test_init (&argc, &argv, NULL); + + g_test_bug_base ("http://bugzilla.gnome.org/"); + + register_list_store_tests (); + register_tree_store_tests (); + register_model_ref_count_tests (); + register_sort_model_tests (); + register_filter_model_tests (); + + return g_test_run (); +} + +/* + * Signal monitor + */ + +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; + + /* For rows-reordered */ + int *new_order; + int len; +} +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); + s->new_order = NULL; + + return s; +} + +static Signal * +signal_new_with_order (SignalName signal, GtkTreePath *path, + int *new_order, int len) +{ + Signal *s = signal_new (signal, path); + + s->new_order = new_order; + s->len = len; + + return s; +} + +static void +signal_free (Signal *s) +{ + if (s->path) + gtk_tree_path_free (s->path); + + g_free (s); +} + + +struct _SignalMonitor +{ + GQueue *queue; + GtkTreeModel *client; + gulong signal_ids[LAST_SIGNAL]; +}; + + +static void +signal_monitor_generic_handler (SignalMonitor *m, + SignalName signal, + GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreePath *path, + int *new_order) +{ + 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_get_depth (s->path) == 0 && + gtk_tree_path_get_depth (path) != 0) || + (gtk_tree_path_get_depth (s->path) != 0 && + 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 (); + } + + if (signal == ROWS_REORDERED && s->new_order != NULL) + { + int i, len; + + g_assert (new_order != NULL); + + len = gtk_tree_model_iter_n_children (model, iter); + g_assert (s->len == len); + + for (i = 0; i < len; i++) + g_assert (s->new_order[i] == new_order[i]); + } + + 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, iter, path, NULL); +} + +static void +signal_monitor_row_deleted (GtkTreeModel *model, + GtkTreePath *path, + gpointer data) +{ + signal_monitor_generic_handler (data, ROW_DELETED, + model, NULL, path, NULL); +} + +static void +signal_monitor_row_changed (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + signal_monitor_generic_handler (data, ROW_CHANGED, + model, iter, path, NULL); +} + +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, iter, path, NULL); +} + +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, iter, path, new_order); +} + +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; +} + +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); +} + +void +signal_monitor_assert_is_empty (SignalMonitor *m) +{ + g_assert (g_queue_is_empty (m->queue)); +} + +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); +} + +void +signal_monitor_append_signal_reordered (SignalMonitor *m, + SignalName signal, + GtkTreePath *path, + int *new_order, + int len) +{ + Signal *s; + + s = signal_new_with_order (signal, path, new_order, len); + g_queue_push_head (m->queue, s); +} + +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); +} diff --git a/gtk/tests/treemodel.h b/gtk/tests/treemodel.h new file mode 100644 index 0000000000..1478f470b5 --- /dev/null +++ b/gtk/tests/treemodel.h @@ -0,0 +1,60 @@ +/* Main wrapper for TreeModel test suite. + * Copyright (C) 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 + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + */ + +#include <gtk/gtk.h> + +void register_list_store_tests (); +void register_tree_store_tests (); +void register_sort_model_tests (); +void register_filter_model_tests (); +void register_model_ref_count_tests (); + +/* + * Signal monitor + */ +typedef struct _SignalMonitor SignalMonitor; +typedef enum _SignalName SignalName; + +enum _SignalName +{ + ROW_INSERTED, + ROW_DELETED, + ROW_CHANGED, + ROW_HAS_CHILD_TOGGLED, + ROWS_REORDERED, + LAST_SIGNAL +}; + + +SignalMonitor *signal_monitor_new (GtkTreeModel *client); +void signal_monitor_free (SignalMonitor *m); + +void signal_monitor_assert_is_empty (SignalMonitor *m); + +void signal_monitor_append_signal_reordered (SignalMonitor *m, + SignalName signal, + GtkTreePath *path, + int *new_order, + int len); +void signal_monitor_append_signal_path (SignalMonitor *m, + SignalName signal, + GtkTreePath *path); +void signal_monitor_append_signal (SignalMonitor *m, + SignalName signal, + const gchar *path_string); diff --git a/gtk/tests/treestore.c b/gtk/tests/treestore.c index 663eaaf580..9d0c4c3d4e 100644 --- a/gtk/tests/treestore.c +++ b/gtk/tests/treestore.c @@ -998,131 +998,162 @@ tree_store_test_iter_parent_invalid (TreeStore *fixture, g_assert (iter.stamp == 0); } +/* specific bugs */ +static void +specific_bug_77977 (void) +{ + GtkTreeStore *tree_store; + GtkTreeIter iter1, iter2, iter3; + GtkTreePath *path; + GtkTreeRowReference *row_ref; + + /* Stripped down version of test case for bug 77977 by Damon Chaplin */ + + g_test_bug ("77977"); + + tree_store = gtk_tree_store_new (1, G_TYPE_STRING); + + gtk_tree_store_append (tree_store, &iter1, NULL); + gtk_tree_store_set (tree_store, &iter1, 0, "Window1", -1); + + gtk_tree_store_append (tree_store, &iter2, &iter1); + gtk_tree_store_set (tree_store, &iter2, 0, "Table1", -1); + + gtk_tree_store_append (tree_store, &iter3, &iter2); + gtk_tree_store_set (tree_store, &iter3, 0, "Button1", -1); + + path = gtk_tree_path_new_from_indices (0, 0, 0, -1); + row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (tree_store), path); + gtk_tree_path_free (path); + + gtk_tree_store_remove (tree_store, &iter1); + + gtk_tree_row_reference_free (row_ref); + g_object_unref (tree_store); +} /* main */ -int -main (int argc, - char **argv) +void +register_tree_store_tests (void) { - gtk_test_init (&argc, &argv, NULL); - /* insertion */ - g_test_add_func ("/tree-store/insert-high-values", + g_test_add_func ("/TreeStore/insert-high-values", tree_store_test_insert_high_values); - g_test_add_func ("/tree-store/append", + g_test_add_func ("/TreeStore/append", tree_store_test_append); - g_test_add_func ("/tree-store/prepend", + g_test_add_func ("/TreeStore/prepend", tree_store_test_prepend); - g_test_add_func ("/tree-store/insert-after", + g_test_add_func ("/TreeStore/insert-after", tree_store_test_insert_after); - g_test_add_func ("/tree-store/insert-after-NULL", + g_test_add_func ("/TreeStore/insert-after-NULL", tree_store_test_insert_after_NULL); - g_test_add_func ("/tree-store/insert-before", + g_test_add_func ("/TreeStore/insert-before", tree_store_test_insert_before); - g_test_add_func ("/tree-store/insert-before-NULL", + g_test_add_func ("/TreeStore/insert-before-NULL", tree_store_test_insert_before_NULL); /* setting values (FIXME) */ /* removal */ - g_test_add ("/tree-store/remove-begin", TreeStore, NULL, + g_test_add ("/TreeStore/remove-begin", TreeStore, NULL, tree_store_setup, tree_store_test_remove_begin, tree_store_teardown); - g_test_add ("/tree-store/remove-middle", TreeStore, NULL, + g_test_add ("/TreeStore/remove-middle", TreeStore, NULL, tree_store_setup, tree_store_test_remove_middle, tree_store_teardown); - g_test_add ("/tree-store/remove-end", TreeStore, NULL, + g_test_add ("/TreeStore/remove-end", TreeStore, NULL, tree_store_setup, tree_store_test_remove_end, tree_store_teardown); - g_test_add ("/tree-store/clear", TreeStore, NULL, + g_test_add ("/TreeStore/clear", TreeStore, NULL, tree_store_setup, tree_store_test_clear, tree_store_teardown); /* reordering */ - g_test_add ("/tree-store/reorder", TreeStore, NULL, + g_test_add ("/TreeStore/reorder", TreeStore, NULL, tree_store_setup, tree_store_test_reorder, tree_store_teardown); /* swapping */ - g_test_add ("/tree-store/swap-begin", TreeStore, NULL, + g_test_add ("/TreeStore/swap-begin", TreeStore, NULL, tree_store_setup, tree_store_test_swap_begin, tree_store_teardown); - g_test_add ("/tree-store/swap-middle-next", TreeStore, NULL, + g_test_add ("/TreeStore/swap-middle-next", TreeStore, NULL, tree_store_setup, tree_store_test_swap_middle_next, tree_store_teardown); - g_test_add ("/tree-store/swap-middle-apart", TreeStore, NULL, + g_test_add ("/TreeStore/swap-middle-apart", TreeStore, NULL, tree_store_setup, tree_store_test_swap_middle_apart, tree_store_teardown); - g_test_add ("/tree-store/swap-end", TreeStore, NULL, + g_test_add ("/TreeStore/swap-end", TreeStore, NULL, tree_store_setup, tree_store_test_swap_end, tree_store_teardown); - g_test_add_func ("/tree-store/swap-single", + g_test_add_func ("/TreeStore/swap-single", tree_store_test_swap_single); /* moving */ - g_test_add ("/tree-store/move-after-from-start", TreeStore, NULL, + g_test_add ("/TreeStore/move-after-from-start", TreeStore, NULL, tree_store_setup, tree_store_test_move_after_from_start, tree_store_teardown); - g_test_add ("/tree-store/move-after-next", TreeStore, NULL, + g_test_add ("/TreeStore/move-after-next", TreeStore, NULL, tree_store_setup, tree_store_test_move_after_next, tree_store_teardown); - g_test_add ("/tree-store/move-after-apart", TreeStore, NULL, + g_test_add ("/TreeStore/move-after-apart", TreeStore, NULL, tree_store_setup, tree_store_test_move_after_apart, tree_store_teardown); - g_test_add ("/tree-store/move-after-end", TreeStore, NULL, + g_test_add ("/TreeStore/move-after-end", TreeStore, NULL, tree_store_setup, tree_store_test_move_after_end, tree_store_teardown); - g_test_add ("/tree-store/move-after-from-end", TreeStore, NULL, + g_test_add ("/TreeStore/move-after-from-end", TreeStore, NULL, tree_store_setup, tree_store_test_move_after_from_end, tree_store_teardown); - g_test_add ("/tree-store/move-after-change-ends", TreeStore, NULL, + g_test_add ("/TreeStore/move-after-change-ends", TreeStore, NULL, tree_store_setup, tree_store_test_move_after_change_ends, tree_store_teardown); - g_test_add ("/tree-store/move-after-NULL", TreeStore, NULL, + g_test_add ("/TreeStore/move-after-NULL", TreeStore, NULL, tree_store_setup, tree_store_test_move_after_NULL, tree_store_teardown); - g_test_add_func ("/tree-store/move-after-single", + g_test_add_func ("/TreeStore/move-after-single", tree_store_test_move_after_single); - g_test_add ("/tree-store/move-before-next", TreeStore, NULL, + g_test_add ("/TreeStore/move-before-next", TreeStore, NULL, tree_store_setup, tree_store_test_move_before_next, tree_store_teardown); - g_test_add ("/tree-store/move-before-apart", TreeStore, NULL, + g_test_add ("/TreeStore/move-before-apart", TreeStore, NULL, tree_store_setup, tree_store_test_move_before_apart, tree_store_teardown); - g_test_add ("/tree-store/move-before-to-start", TreeStore, NULL, + g_test_add ("/TreeStore/move-before-to-start", TreeStore, NULL, tree_store_setup, tree_store_test_move_before_to_start, tree_store_teardown); - g_test_add ("/tree-store/move-before-from-end", TreeStore, NULL, + g_test_add ("/TreeStore/move-before-from-end", TreeStore, NULL, tree_store_setup, tree_store_test_move_before_from_end, tree_store_teardown); - g_test_add ("/tree-store/move-before-change-ends", TreeStore, NULL, + g_test_add ("/TreeStore/move-before-change-ends", TreeStore, NULL, tree_store_setup, tree_store_test_move_before_change_ends, tree_store_teardown); - g_test_add ("/tree-store/move-before-NULL", TreeStore, NULL, + g_test_add ("/TreeStore/move-before-NULL", TreeStore, NULL, tree_store_setup, tree_store_test_move_before_NULL, tree_store_teardown); - g_test_add_func ("/tree-store/move-before-single", + g_test_add_func ("/TreeStore/move-before-single", tree_store_test_move_before_single); /* iter invalidation */ - g_test_add ("/tree-store/iter-prev-invalid", TreeStore, NULL, + g_test_add ("/TreeStore/iter-prev-invalid", TreeStore, NULL, tree_store_setup, tree_store_test_iter_previous_invalid, tree_store_teardown); - g_test_add ("/tree-store/iter-next-invalid", TreeStore, NULL, + g_test_add ("/TreeStore/iter-next-invalid", TreeStore, NULL, tree_store_setup, tree_store_test_iter_next_invalid, tree_store_teardown); - g_test_add ("/tree-store/iter-children-invalid", TreeStore, NULL, + g_test_add ("/TreeStore/iter-children-invalid", TreeStore, NULL, tree_store_setup, tree_store_test_iter_children_invalid, tree_store_teardown); - g_test_add ("/tree-store/iter-nth-child-invalid", TreeStore, NULL, + g_test_add ("/TreeStore/iter-nth-child-invalid", TreeStore, NULL, tree_store_setup, tree_store_test_iter_nth_child_invalid, tree_store_teardown); - g_test_add ("/tree-store/iter-parent-invalid", TreeStore, NULL, + g_test_add ("/TreeStore/iter-parent-invalid", TreeStore, NULL, tree_store_setup, tree_store_test_iter_parent_invalid, tree_store_teardown); - return g_test_run (); + /* specific bugs */ + g_test_add_func ("/TreeStore/bug-77977", specific_bug_77977); } |