testsuite: Add section tests to sortlistmodel test

This commit is contained in:
Benjamin Otte 2022-02-19 02:37:53 +01:00
parent a09146df5a
commit 41faddea40

View File

@ -36,6 +36,8 @@
if (o1 != o2) \
{ \
char *_s = g_strdup_printf ("Objects differ at index %u out of %u", _i, _n); \
g_print ("%s\n", model_to_string (model1)); \
g_print ("%s\n", model_to_string (model2)); \
g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, _s); \
g_free (_s); \
} \
@ -45,6 +47,19 @@
} \
}G_STMT_END
#define assert_model_sections(model) G_STMT_START{ \
guint _i, _start, _end; \
_start = 0; \
_end = 0; \
for (_i = 0; _i < G_MAXUINT; _i = _end) \
{ \
gtk_section_model_get_section (GTK_SECTION_MODEL (model), _i, &_start, &_end); \
\
g_assert_cmpint (_start, ==, _i); \
g_assert_cmpint (_end, >, _i); \
} \
}G_STMT_END
G_GNUC_UNUSED static char *
model_to_string (GListModel *model)
{
@ -287,7 +302,7 @@ create_sorter (gsize id)
/* match all As, Bs and nothing */
sorter = GTK_SORTER (gtk_string_sorter_new (gtk_property_expression_new (GTK_TYPE_STRING_OBJECT, NULL, "string")));
if (id == 1)
gtk_string_sorter_set_ignore_case (GTK_STRING_SORTER (sorter), TRUE);
gtk_string_sorter_set_ignore_case (GTK_STRING_SORTER (sorter), FALSE);
return sorter;
default:
@ -463,6 +478,258 @@ test_stability (gconstpointer model_id)
g_object_unref (flatten);
}
static gboolean
string_is_lowercase (GtkStringObject *o)
{
return g_ascii_islower (*gtk_string_object_get_string (o));
}
/* Run:
* source => section-sorter
* source => sorter
* and set a section sorter on the section sorter that is a subsort of
* the real sorter.
*
* And then randomly add/remove sources and change the sorters and
* see if the two sorters stay identical
*/
static void
test_section_sorters (gconstpointer model_id)
{
GListStore *store;
GtkFlattenListModel *flatten;
GtkSortListModel *sort1, *sort2;
GtkSorter *sorter;
gsize i;
store = g_list_store_new (G_TYPE_OBJECT);
flatten = gtk_flatten_list_model_new (G_LIST_MODEL (store));
sort1 = create_sort_list_model (model_id, FALSE, G_LIST_MODEL (flatten), NULL);
sort2 = create_sort_list_model (model_id, FALSE, G_LIST_MODEL (flatten), NULL);
for (i = 0; i < 500; i++)
{
gboolean add = FALSE, remove = FALSE;
guint position;
switch (g_test_rand_int_range (0, 4))
{
case 0:
/* set the same sorter, once as section sorter, once as sorter */
sorter = create_random_sorter (TRUE);
gtk_sort_list_model_set_section_sorter (sort1, sorter);
gtk_sort_list_model_set_sorter (sort1, NULL);
gtk_sort_list_model_set_sorter (sort2, sorter);
g_clear_object (&sorter);
break;
case 1:
/* use a section sorter that is a more generic version of the sorter */
sorter = GTK_SORTER (gtk_string_sorter_new (gtk_property_expression_new (GTK_TYPE_STRING_OBJECT, NULL, "string")));
gtk_string_sorter_set_ignore_case (GTK_STRING_SORTER (sorter), FALSE);
gtk_sort_list_model_set_sorter (sort1, sorter);
gtk_sort_list_model_set_sorter (sort2, sorter);
g_clear_object (&sorter);
sorter = GTK_SORTER (gtk_numeric_sorter_new (gtk_cclosure_expression_new (G_TYPE_BOOLEAN, NULL, 0, NULL, G_CALLBACK (string_is_lowercase), NULL, NULL)));
gtk_sort_list_model_set_section_sorter (sort1, sorter);
g_clear_object (&sorter);
break;
case 2:
/* remove a model */
remove = TRUE;
break;
case 3:
/* add a model */
add = TRUE;
break;
case 4:
/* 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 (store)) + 1);
if (g_list_model_get_n_items (G_LIST_MODEL (store)) == position)
remove = FALSE;
if (add)
{
/* We want at least one element, otherwise the sorters will see no changes */
GListModel *source = create_source_model (1, 50);
g_list_store_splice (store,
position,
remove ? 1 : 0,
(gpointer *) &source, 1);
g_object_unref (source);
}
else if (remove)
{
g_list_store_remove (store, position);
}
if (g_test_rand_bit ())
{
ensure_updated ();
assert_model_equal (G_LIST_MODEL (sort1), G_LIST_MODEL (sort2));
}
if (g_test_rand_bit ())
assert_model_sections (G_LIST_MODEL (sort1));
}
g_object_unref (sort2);
g_object_unref (sort1);
g_object_unref (flatten);
}
/* Run:
* source => sorter
* And then randomly add/remove sources and change the sorters and
* see if the invariants for sections keep correct.
*/
static void
test_sections (gconstpointer model_id)
{
GListStore *store;
GtkFlattenListModel *flatten;
GtkSortListModel *sort;
GtkSorter *sorter;
gsize i;
store = g_list_store_new (G_TYPE_OBJECT);
flatten = gtk_flatten_list_model_new (G_LIST_MODEL (store));
sort = create_sort_list_model (model_id, FALSE, G_LIST_MODEL (flatten), NULL);
for (i = 0; i < 500; i++)
{
gboolean add = FALSE, remove = FALSE;
guint position;
switch (g_test_rand_int_range (0, 4))
{
case 0:
/* set the same sorter, once as section sorter, once as sorter */
sorter = create_random_sorter (TRUE);
gtk_sort_list_model_set_sorter (sort, sorter);
g_clear_object (&sorter);
break;
case 1:
/* set the same sorter, once as section sorter, once as sorter */
sorter = create_random_sorter (TRUE);
gtk_sort_list_model_set_section_sorter (sort, sorter);
g_clear_object (&sorter);
break;
case 2:
/* remove a model */
remove = TRUE;
break;
case 3:
/* add a model */
add = TRUE;
break;
case 4:
/* 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 (store)) + 1);
if (g_list_model_get_n_items (G_LIST_MODEL (store)) == position)
remove = FALSE;
if (add)
{
/* We want at least one element, otherwise the sorters will see no changes */
GListModel *source = create_source_model (1, 50);
g_list_store_splice (store,
position,
remove ? 1 : 0,
(gpointer *) &source, 1);
g_object_unref (source);
}
else if (remove)
{
g_list_store_remove (store, position);
}
if (g_test_rand_bit ())
ensure_updated ();
if (g_test_rand_bit ())
{
guint start, end, pos, n, sec_start, sec_end;
gpointer prev_item, item;
n = g_list_model_get_n_items (G_LIST_MODEL (sort));
sorter = gtk_sort_list_model_get_section_sorter (sort);
start = end = 0;
prev_item = item = NULL;
for (pos = 0; pos < n; pos++)
{
gtk_section_model_get_section (GTK_SECTION_MODEL (sort), pos, &sec_start, &sec_end);
prev_item = item;
item = g_list_model_get_item (G_LIST_MODEL (sort), pos);
if (end <= pos)
{
g_assert_cmpint (pos, ==, end);
/* there should be a new section */
g_assert_cmpint (sec_start, ==, end);
g_assert_cmpint (sec_end, >, sec_start);
g_assert_cmpint (sec_end, <=, n);
start = sec_start;
end = sec_end;
if (prev_item)
{
g_assert_nonnull (sorter);
g_assert_cmpint (gtk_sorter_compare (sorter, prev_item, item), !=, GTK_ORDERING_EQUAL);
}
}
else
{
/* the old section keeps on going */
g_assert_cmpint (sec_start, ==, start);
g_assert_cmpint (sec_end, ==, end);
if (prev_item && sorter)
g_assert_cmpint (gtk_sorter_compare (sorter, prev_item, item), ==, GTK_ORDERING_EQUAL);
}
g_clear_object (&prev_item);
}
g_clear_object (&item);
/* for good measure, check the error condition */
if (n < G_MAXINT32)
{
gtk_section_model_get_section (GTK_SECTION_MODEL (sort), g_test_rand_int_range (n, G_MAXINT32), &sec_start, &sec_end);
g_assert_cmpint (sec_start, ==, n);
g_assert_cmpint (sec_end, ==, G_MAXUINT);
}
sorter = NULL;
}
}
g_object_unref (sort);
g_object_unref (flatten);
}
static void
add_test_for_all_models (const char *name,
GTestDataFunc test_func)
@ -488,6 +755,8 @@ main (int argc, char *argv[])
add_test_for_all_models ("two-sorters", test_two_sorters);
add_test_for_all_models ("stability", test_stability);
add_test_for_all_models ("section-sorters", test_section_sorters);
add_test_for_all_models ("sections", test_sections);
return g_test_run ();
}