diff options
author | Benjamin Otte <otte@redhat.com> | 2020-07-05 19:25:15 +0200 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2020-07-06 02:52:13 +0200 |
commit | 0628b4a43e3bdee3fc6056590912e813d60b4a7d (patch) | |
tree | 8c4504373221587806fdcab9e57b860b5081ca54 | |
parent | b51bfae6d849642119717cd4d4314f8904d295d8 (diff) | |
download | gtk+-wip/otte/whatever.tar.gz |
testsuite: Add more filterlistmodel testswip/otte/whatever
These ones try to be exhaustive and randomly catch weird cases.
As such, the tests are more complicated and harder to grasp.
Sorry.
-rw-r--r-- | testsuite/gtk/filterlistmodel-exhaustive.c | 479 | ||||
-rw-r--r-- | testsuite/gtk/meson.build | 1 |
2 files changed, 480 insertions, 0 deletions
diff --git a/testsuite/gtk/filterlistmodel-exhaustive.c b/testsuite/gtk/filterlistmodel-exhaustive.c new file mode 100644 index 0000000000..83b74b7c70 --- /dev/null +++ b/testsuite/gtk/filterlistmodel-exhaustive.c @@ -0,0 +1,479 @@ +/* GtkRBTree tests. + * + * Copyright (C) 2011, Red Hat, Inc. + * Authors: Benjamin Otte <otte@gnome.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, see <http://www.gnu.org/licenses/>. + */ + +#include <locale.h> + +#include <gtk/gtk.h> + +#define ensure_updated() G_STMT_START{ \ + while (g_main_context_pending (NULL)) \ + g_main_context_iteration (NULL, TRUE); \ +}G_STMT_END + +#define assert_model_equal(model1, model2) G_STMT_START{ \ + guint _i, _n; \ + g_assert_cmpint (g_list_model_get_n_items (model1), ==, g_list_model_get_n_items (model2)); \ + _n = g_list_model_get_n_items (model1); \ + for (_i = 0; _i < _n; _i++) \ + { \ + gpointer o1 = g_list_model_get_item (model1, _i); \ + gpointer o2 = g_list_model_get_item (model2, _i); \ + if (o1 != o2) \ + { \ + char *_s = g_strdup_printf ("Objects differ at index %u out of %u", _i, _n); \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, _s); \ + g_free (_s); \ + } \ + } \ +}G_STMT_END + +G_GNUC_UNUSED static char * +model_to_string (GListModel *model) +{ + GString *string; + guint i, n; + + n = g_list_model_get_n_items (model); + string = g_string_new (NULL); + + /* Check that all unchanged items are indeed unchanged */ + for (i = 0; i < n; i++) + { + gpointer item = g_list_model_get_item (model, i); + + if (i > 0) + g_string_append (string, ", "); + g_string_append (string, gtk_string_object_get_string (item)); + g_object_unref (item); + } + + return g_string_free (string, FALSE); +} + +static void +assert_items_changed_correctly (GListModel *model, + guint position, + guint removed, + guint added, + GListModel *compare) +{ + guint i, n_items; + + //g_print ("%s => %u -%u +%u => %s\n", model_to_string (compare), position, removed, added, model_to_string (model)); + + g_assert_cmpint (g_list_model_get_n_items (model), ==, g_list_model_get_n_items (compare) - removed + added); + n_items = g_list_model_get_n_items (model); + + /* Check that all unchanged items are indeed unchanged */ + for (i = 0; i < position; i++) + { + gpointer o1 = g_list_model_get_item (model, i); + gpointer o2 = g_list_model_get_item (compare, i); + g_assert_cmphex (GPOINTER_TO_SIZE (o1), ==, GPOINTER_TO_SIZE (o2)); + g_object_unref (o1); + g_object_unref (o2); + } + for (i = position + added; i < n_items; i++) + { + gpointer o1 = g_list_model_get_item (model, i); + gpointer o2 = g_list_model_get_item (compare, i - added + removed); + g_assert_cmphex (GPOINTER_TO_SIZE (o1), ==, GPOINTER_TO_SIZE (o2)); + g_object_unref (o1); + g_object_unref (o2); + } + + /* Check that the first and last added item are different from + * first and last removed item. + * Otherwise we could have kept them as-is + */ + if (removed > 0 && added > 0) + { + gpointer o1 = g_list_model_get_item (model, position); + gpointer o2 = g_list_model_get_item (compare, position); + g_assert_cmphex (GPOINTER_TO_SIZE (o1), !=, GPOINTER_TO_SIZE (o2)); + g_object_unref (o1); + g_object_unref (o2); + + o1 = g_list_model_get_item (model, position + added - 1); + o2 = g_list_model_get_item (compare, position + removed - 1); + g_assert_cmphex (GPOINTER_TO_SIZE (o1), !=, GPOINTER_TO_SIZE (o2)); + g_object_unref (o1); + g_object_unref (o2); + } + + /* Finally, perform the same change as the signal indicates */ + g_list_store_splice (G_LIST_STORE (compare), position, removed, NULL, 0); + for (i = position; i < position + added; i++) + { + gpointer item = g_list_model_get_item (G_LIST_MODEL (model), i); + g_list_store_insert (G_LIST_STORE (compare), i, item); + g_object_unref (item); + } +} + +static GtkFilterListModel * +filter_list_model_new (GListModel *source, + GtkFilter *filter) +{ + GtkFilterListModel *model; + GListStore *check; + guint i; + + model = gtk_filter_list_model_new (source, filter); + check = g_list_store_new (G_TYPE_OBJECT); + for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (model)); i++) + { + gpointer item = g_list_model_get_item (G_LIST_MODEL (model), i); + g_list_store_append (check, item); + g_object_unref (item); + } + g_signal_connect_data (model, + "items-changed", + G_CALLBACK (assert_items_changed_correctly), + check, + (GClosureNotify) g_object_unref, + 0); + + return model; +} + +#define N_MODELS 8 + +static GtkFilterListModel * +create_filter_list_model (gconstpointer model_id, + GListModel *source, + GtkFilter *filter) +{ + GtkFilterListModel *model; + guint id = GPOINTER_TO_UINT (model_id); + + model = filter_list_model_new (id & 1 ? NULL : source, id & 2 ? NULL : filter); + + switch (id >> 2) + { + case 0: + break; + + case 1: + gtk_filter_list_model_set_incremental (model, TRUE); + break; + + default: + g_assert_not_reached (); + break; + } + + if (id & 1) + gtk_filter_list_model_set_model (model, source); + if (id & 2) + gtk_filter_list_model_set_filter (model, filter); + + return model; +} + +static GListModel * +create_source_model (guint min_size, guint max_size) +{ + GtkStringList *list; + guint i, size; + + size = g_random_int_range (min_size, max_size + 1); + list = gtk_string_list_new (NULL); + + for (i = 0; i < size; i++) + gtk_string_list_append (list, g_random_boolean () ? "A" : "B"); + + return G_LIST_MODEL (list); +} + +#define N_FILTERS 5 + +static GtkFilter * +create_filter (gsize id) +{ + GtkFilter *filter; + GtkExpression *expr; + + switch (id) + { + case 0: + /* GTK_FILTER_MATCH_ALL */ + return gtk_string_filter_new (); + + case 1: + /* GTK_FILTER_MATCH_NONE */ + filter = gtk_string_filter_new (); + gtk_string_filter_set_search (GTK_STRING_FILTER (filter), "does not matter, because no expression"); + return filter; + + case 2: + case 3: + case 4: + /* match all As, Bs and nothing */ + filter = gtk_string_filter_new (); + expr = gtk_property_expression_new (GTK_TYPE_STRING_OBJECT, NULL, "string"); + gtk_string_filter_set_expression (GTK_STRING_FILTER (filter), expr); + gtk_expression_unref (expr); + if (id == 2) + gtk_string_filter_set_search (GTK_STRING_FILTER (filter), "A"); + else if (id == 3) + gtk_string_filter_set_search (GTK_STRING_FILTER (filter), "B"); + else + gtk_string_filter_set_search (GTK_STRING_FILTER (filter), "does-not-match"); + return filter; + + default: + g_assert_not_reached (); + return NULL; + } +} + +static GtkFilter * +create_random_filter (gboolean allow_null) +{ + guint n; + + if (allow_null) + n = g_random_int_range (0, N_FILTERS + 1); + else + n = g_random_int_range (0, N_FILTERS); + + if (n >= N_FILTERS) + return NULL; + + return create_filter (n); +} + +static void +test_no_filter (gconstpointer model_id) +{ + GtkFilterListModel *model; + GListModel *source; + GtkFilter *filter; + + source = create_source_model (10, 10); + model = create_filter_list_model (model_id, source, NULL); + ensure_updated (); + assert_model_equal (G_LIST_MODEL (model), source); + + filter = create_random_filter (FALSE); + gtk_filter_list_model_set_filter (model, filter); + g_object_unref (filter); + gtk_filter_list_model_set_filter (model, NULL); + ensure_updated (); + assert_model_equal (G_LIST_MODEL (model), source); + + g_object_unref (model); + g_object_unref (source); +} + +/* Compare this: + * source => filter1 => filter2 + * with: + * source => multifilter(filter1, filter2) + * and randomly change the source and filters and see if the + * two continue agreeing. + */ +static void +test_two_filters (gconstpointer model_id) +{ + GtkFilterListModel *compare; + GtkFilterListModel *model1, *model2; + GListModel *source; + GtkFilter *every, *filter; + guint i, j, k; + + source = create_source_model (10, 10); + model1 = create_filter_list_model (model_id, source, NULL); + model2 = create_filter_list_model (model_id, G_LIST_MODEL (model1), NULL); + every = gtk_every_filter_new (); + compare = create_filter_list_model (model_id, source, every); + g_object_unref (every); + g_object_unref (source); + + for (i = 0; i < N_FILTERS; i++) + { + filter = create_filter (i); + gtk_filter_list_model_set_filter (model1, filter); + gtk_multi_filter_append (GTK_MULTI_FILTER (every), filter); + + for (j = 0; j < N_FILTERS; j++) + { + filter = create_filter (i); + gtk_filter_list_model_set_filter (model2, filter); + gtk_multi_filter_append (GTK_MULTI_FILTER (every), filter); + + ensure_updated (); + assert_model_equal (G_LIST_MODEL (model2), G_LIST_MODEL (compare)); + + for (k = 0; k < 10; k++) + { + source = create_source_model (0, 20); + gtk_filter_list_model_set_model (compare, source); + gtk_filter_list_model_set_model (model1, source); + g_object_unref (source); + + ensure_updated (); + assert_model_equal (G_LIST_MODEL (model2), G_LIST_MODEL (compare)); + } + + gtk_multi_filter_remove (GTK_MULTI_FILTER (every), 1); + } + + gtk_multi_filter_remove (GTK_MULTI_FILTER (every), 0); + } + + g_object_unref (compare); + g_object_unref (model2); + g_object_unref (model1); +} + +/* Compare this: + * (source => filter) * => flatten + * with: + * source * => flatten => filter + * and randomly add/remove sources and change the filters and + * see if the two agree. + * + * We use a multifilter for the top chain so that changing the filter + * is easy. + */ +static void +test_model_changes (gconstpointer model_id) +{ + GListStore *store1, *store2; + GtkFlattenListModel *flatten1, *flatten2; + GtkFilterListModel *model2; + GtkFilter *multi, *filter; + gsize i; + + filter = create_random_filter (TRUE); + multi = gtk_every_filter_new (); + if (filter) + gtk_multi_filter_append (GTK_MULTI_FILTER (multi), filter); + + store1 = g_list_store_new (G_TYPE_OBJECT); + store2 = g_list_store_new (G_TYPE_OBJECT); + flatten1 = gtk_flatten_list_model_new (G_LIST_MODEL (store1)); + flatten2 = gtk_flatten_list_model_new (G_LIST_MODEL (store2)); + model2 = create_filter_list_model (model_id, G_LIST_MODEL (flatten2), filter); + + for (i = 0; i < 500; i++) + { + gboolean add = FALSE, remove = FALSE; + guint position; + + switch (g_random_int_range (0, 4)) + { + case 0: + /* change the filter */ + filter = create_random_filter (TRUE); + gtk_multi_filter_remove (GTK_MULTI_FILTER (multi), 0); /* no-op if no filter */ + if (filter) + gtk_multi_filter_append (GTK_MULTI_FILTER (multi), filter); + gtk_filter_list_model_set_filter (model2, filter); + break; + + case 1: + /* remove a model */ + remove = TRUE; + break; + + case 2: + /* add a model */ + add = TRUE; + break; + + case 3: + /* replace a model */ + remove = TRUE; + add = TRUE; + break; + + default: + g_assert_not_reached (); + break; + } + + position = g_random_int_range (0, g_list_model_get_n_items (G_LIST_MODEL (store1)) + 1); + if (g_list_model_get_n_items (G_LIST_MODEL (store1)) == position) + remove = FALSE; + + if (add) + { + /* We want at least one element, otherwise the filters will see no changes */ + GListModel *source = create_source_model (1, 20); + GtkFilterListModel *model1 = create_filter_list_model (model_id, source, multi); + g_list_store_splice (store1, + position, + remove ? 1 : 0, + (gpointer *) &model1, 1); + g_list_store_splice (store2, + position, + remove ? 1 : 0, + (gpointer *) &source, 1); + g_object_unref (source); + } + else if (remove) + { + g_list_store_remove (store1, position); + g_list_store_remove (store2, position); + } + + if (g_random_boolean ()) + { + ensure_updated (); + assert_model_equal (G_LIST_MODEL (flatten1), G_LIST_MODEL (model2)); + } + } + + g_object_unref (model2); + g_object_unref (flatten2); + g_object_unref (flatten1); + g_object_unref (store2); + g_object_unref (store1); + g_object_unref (multi); +} + +static void +add_test_for_all_models (const char *name, + GTestDataFunc test_func) +{ + guint i; + + for (i = 0; i < N_MODELS; i++) + { + char *path = g_strdup_printf ("/filterlistmodel/model%u/%s", i, name); + g_test_add_data_func (path, GUINT_TO_POINTER (i), test_func); + g_free (path); + } +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + setlocale (LC_ALL, "C"); + + add_test_for_all_models ("no-filter", test_no_filter); + add_test_for_all_models ("two-filters", test_two_filters); + add_test_for_all_models ("model-changes", test_model_changes); + + return g_test_run (); +} diff --git a/testsuite/gtk/meson.build b/testsuite/gtk/meson.build index 6bdab866de..ca1f8c28ea 100644 --- a/testsuite/gtk/meson.build +++ b/testsuite/gtk/meson.build @@ -30,6 +30,7 @@ tests = [ ['expression'], ['filter'], ['filterlistmodel'], + ['filterlistmodel-exhaustive'], ['flattenlistmodel'], ['floating'], ['flowbox'], |