mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-28 14:31:10 +00:00
testsuite: Add more filterlistmodel tests
These ones try to be exhaustive and randomly catch weird cases. As such, the tests are more complicated and harder to grasp. Sorry.
This commit is contained in:
parent
3162e25671
commit
bf3382a89e
479
testsuite/gtk/filterlistmodel-exhaustive.c
Normal file
479
testsuite/gtk/filterlistmodel-exhaustive.c
Normal file
@ -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_test_rand_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_test_rand_bit () ? "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_test_rand_int_range (0, N_FILTERS + 1);
|
||||
else
|
||||
n = g_test_rand_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_test_rand_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_test_rand_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_test_rand_bit ())
|
||||
{
|
||||
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 ();
|
||||
}
|
@ -30,6 +30,7 @@ tests = [
|
||||
['expression'],
|
||||
['filter'],
|
||||
['filterlistmodel'],
|
||||
['filterlistmodel-exhaustive'],
|
||||
['flattenlistmodel'],
|
||||
['floating'],
|
||||
['flowbox'],
|
||||
|
Loading…
Reference in New Issue
Block a user