diff --git a/gio/tests/glistmodel.c b/gio/tests/glistmodel.c index 8e73a121a..dcf571f29 100644 --- a/gio/tests/glistmodel.c +++ b/gio/tests/glistmodel.c @@ -318,6 +318,419 @@ test_store_splice_replace_all (void) g_object_unref (store); } +/* Test that using splice() without removing or adding anything works */ +static void +test_store_splice_noop (void) +{ + GListStore *store; + GListModel *model; + GAction *item; + + store = g_list_store_new (G_TYPE_SIMPLE_ACTION); + model = G_LIST_MODEL (store); + + /* splice noop with an empty list */ + g_list_store_splice (store, 0, 0, NULL, 0); + g_assert_cmpuint (g_list_model_get_n_items (model), ==, 0); + + /* splice noop with a non-empty list */ + item = G_ACTION (g_simple_action_new ("1", NULL)); + g_list_store_append (store, item); + g_object_unref (item); + + g_list_store_splice (store, 0, 0, NULL, 0); + g_assert_cmpuint (g_list_model_get_n_items (model), ==, 1); + + g_list_store_splice (store, 1, 0, NULL, 0); + g_assert_cmpuint (g_list_model_get_n_items (model), ==, 1); + + item = g_list_model_get_item (model, 0); + g_assert_cmpstr (g_action_get_name (item), ==, "1"); + g_object_unref (item); + + g_object_unref (store); +} + +static gboolean +model_array_equal (GListModel *model, GPtrArray *array) +{ + guint i; + + if (g_list_model_get_n_items (model) != array->len) + return FALSE; + + for (i = 0; i < array->len; i++) + { + GObject *ptr; + gboolean ptrs_equal; + + ptr = g_list_model_get_item (model, i); + ptrs_equal = (g_ptr_array_index (array, i) == ptr); + g_object_unref (ptr); + if (!ptrs_equal) + return FALSE; + } + + return TRUE; +} + +/* Test that using splice() to remove multiple items at different + * positions works */ +static void +test_store_splice_remove_multiple (void) +{ + GListStore *store; + GListModel *model; + GPtrArray *array; + + store = g_list_store_new (G_TYPE_SIMPLE_ACTION); + model = G_LIST_MODEL (store); + + array = g_ptr_array_new_full (0, g_object_unref); + g_ptr_array_add (array, g_simple_action_new ("1", NULL)); + g_ptr_array_add (array, g_simple_action_new ("2", NULL)); + g_ptr_array_add (array, g_simple_action_new ("3", NULL)); + g_ptr_array_add (array, g_simple_action_new ("4", NULL)); + g_ptr_array_add (array, g_simple_action_new ("5", NULL)); + g_ptr_array_add (array, g_simple_action_new ("6", NULL)); + g_ptr_array_add (array, g_simple_action_new ("7", NULL)); + g_ptr_array_add (array, g_simple_action_new ("8", NULL)); + g_ptr_array_add (array, g_simple_action_new ("9", NULL)); + g_ptr_array_add (array, g_simple_action_new ("10", NULL)); + + /* Add all */ + g_list_store_splice (store, 0, 0, array->pdata, array->len); + g_assert_true (model_array_equal (model, array)); + + /* Remove the first two */ + g_list_store_splice (store, 0, 2, NULL, 0); + g_assert_false (model_array_equal (model, array)); + g_ptr_array_remove_range (array, 0, 2); + g_assert_true (model_array_equal (model, array)); + g_assert_cmpuint (g_list_model_get_n_items (model), ==, 8); + + /* Remove two in the middle */ + g_list_store_splice (store, 2, 2, NULL, 0); + g_assert_false (model_array_equal (model, array)); + g_ptr_array_remove_range (array, 2, 2); + g_assert_true (model_array_equal (model, array)); + g_assert_cmpuint (g_list_model_get_n_items (model), ==, 6); + + /* Remove two at the end */ + g_list_store_splice (store, 4, 2, NULL, 0); + g_assert_false (model_array_equal (model, array)); + g_ptr_array_remove_range (array, 4, 2); + g_assert_true (model_array_equal (model, array)); + g_assert_cmpuint (g_list_model_get_n_items (model), ==, 4); + + g_ptr_array_unref (array); + g_object_unref (store); +} + +/* Test that using splice() to add multiple items at different + * positions works */ +static void +test_store_splice_add_multiple (void) +{ + GListStore *store; + GListModel *model; + GPtrArray *array; + + store = g_list_store_new (G_TYPE_SIMPLE_ACTION); + model = G_LIST_MODEL (store); + + array = g_ptr_array_new_full (0, g_object_unref); + g_ptr_array_add (array, g_simple_action_new ("1", NULL)); + g_ptr_array_add (array, g_simple_action_new ("2", NULL)); + g_ptr_array_add (array, g_simple_action_new ("3", NULL)); + g_ptr_array_add (array, g_simple_action_new ("4", NULL)); + g_ptr_array_add (array, g_simple_action_new ("5", NULL)); + g_ptr_array_add (array, g_simple_action_new ("6", NULL)); + + /* Add two at the beginning */ + g_list_store_splice (store, 0, 0, array->pdata, 2); + + /* Add two at the end */ + g_list_store_splice (store, 2, 0, array->pdata + 4, 2); + + /* Add two in the middle */ + g_list_store_splice (store, 2, 0, array->pdata + 2, 2); + + g_assert_true (model_array_equal (model, array)); + + g_ptr_array_unref (array); + g_object_unref (store); +} + +/* Test that get_item_type() returns the right type */ +static void +test_store_item_type (void) +{ + GListStore *store; + GType gtype; + + store = g_list_store_new (G_TYPE_SIMPLE_ACTION); + gtype = g_list_model_get_item_type (G_LIST_MODEL (store)); + g_assert (gtype == G_TYPE_SIMPLE_ACTION); + + g_object_unref (store); +} + +/* Test that remove_all() removes all items */ +static void +test_store_remove_all (void) +{ + GListStore *store; + GListModel *model; + GSimpleAction *item; + + store = g_list_store_new (G_TYPE_SIMPLE_ACTION); + model = G_LIST_MODEL (store); + + /* Test with an empty list */ + g_list_store_remove_all (store); + g_assert_cmpuint (g_list_model_get_n_items (model), ==, 0); + + /* Test with a non-empty list */ + item = g_simple_action_new ("42", NULL); + g_list_store_append (store, item); + g_list_store_append (store, item); + g_object_unref (item); + g_assert_cmpuint (g_list_model_get_n_items (model), ==, 2); + g_list_store_remove_all (store); + g_assert_cmpuint (g_list_model_get_n_items (model), ==, 0); + + g_object_unref (store); +} + +/* Test that splice() logs an error when passed the wrong item type */ +static void +test_store_splice_wrong_type (void) +{ + GListStore *store; + + store = g_list_store_new (G_TYPE_SIMPLE_ACTION); + + g_test_expect_message (G_LOG_DOMAIN, + G_LOG_LEVEL_CRITICAL, + "*GListStore instead of a GSimpleAction*"); + g_list_store_splice (store, 0, 0, (gpointer)&store, 1); + + g_object_unref (store); +} + +static gint +ptr_array_cmp_action_by_name (GAction **a, GAction **b) +{ + return g_strcmp0 (g_action_get_name (*a), g_action_get_name (*b)); +} + +static gint +list_model_cmp_action_by_name (GAction *a, GAction *b, gpointer user_data) +{ + return g_strcmp0 (g_action_get_name (a), g_action_get_name (b)); +} + +/* Test if sort() works */ +static void +test_store_sort (void) +{ + GListStore *store; + GListModel *model; + GPtrArray *array; + + store = g_list_store_new (G_TYPE_SIMPLE_ACTION); + model = G_LIST_MODEL (store); + + array = g_ptr_array_new_full (0, g_object_unref); + g_ptr_array_add (array, g_simple_action_new ("2", NULL)); + g_ptr_array_add (array, g_simple_action_new ("3", NULL)); + g_ptr_array_add (array, g_simple_action_new ("9", NULL)); + g_ptr_array_add (array, g_simple_action_new ("4", NULL)); + g_ptr_array_add (array, g_simple_action_new ("5", NULL)); + g_ptr_array_add (array, g_simple_action_new ("8", NULL)); + g_ptr_array_add (array, g_simple_action_new ("6", NULL)); + g_ptr_array_add (array, g_simple_action_new ("7", NULL)); + g_ptr_array_add (array, g_simple_action_new ("1", NULL)); + + /* Sort an empty list */ + g_list_store_sort (store, (GCompareDataFunc)list_model_cmp_action_by_name, NULL); + + /* Add all */ + g_list_store_splice (store, 0, 0, array->pdata, array->len); + g_assert_true (model_array_equal (model, array)); + + /* Sort both and check if the result is the same */ + g_ptr_array_sort (array, (GCompareFunc)ptr_array_cmp_action_by_name); + g_assert_false (model_array_equal (model, array)); + g_list_store_sort (store, (GCompareDataFunc)list_model_cmp_action_by_name, NULL); + g_assert_true (model_array_equal (model, array)); + + g_ptr_array_unref (array); + g_object_unref (store); +} + +/* Test the cases where the item store tries to speed up item access by caching + * the last iter/position */ +static void +test_store_get_item_cache (void) +{ + GListStore *store; + GListModel *model; + GSimpleAction *item1, *item2, *temp; + + store = g_list_store_new (G_TYPE_SIMPLE_ACTION); + model = G_LIST_MODEL (store); + + /* Add two */ + item1 = g_simple_action_new ("1", NULL); + g_list_store_append (store, item1); + item2 = g_simple_action_new ("2", NULL); + g_list_store_append (store, item2); + + /* Clear the cache */ + g_assert_null (g_list_model_get_item (model, 42)); + + /* Access the same position twice */ + temp = g_list_model_get_item (model, 1); + g_assert (temp == item2); + g_object_unref (temp); + temp = g_list_model_get_item (model, 1); + g_assert (temp == item2); + g_object_unref (temp); + + g_assert_null (g_list_model_get_item (model, 42)); + + /* Access forwards */ + temp = g_list_model_get_item (model, 0); + g_assert (temp == item1); + g_object_unref (temp); + temp = g_list_model_get_item (model, 1); + g_assert (temp == item2); + g_object_unref (temp); + + g_assert_null (g_list_model_get_item (model, 42)); + + /* Access backwards */ + temp = g_list_model_get_item (model, 1); + g_assert (temp == item2); + g_object_unref (temp); + temp = g_list_model_get_item (model, 0); + g_assert (temp == item1); + g_object_unref (temp); + + g_object_unref (item1); + g_object_unref (item2); + g_object_unref (store); +} + +struct ItemsChangedData +{ + guint position; + guint removed; + guint added; + gboolean called; +}; + +static void +expect_items_changed (struct ItemsChangedData *expected, + guint position, + guint removed, + guint added) +{ + expected->position = position; + expected->removed = removed; + expected->added = added; + expected->called = FALSE; +} + +static void +on_items_changed (GListModel *model, + guint position, + guint removed, + guint added, + struct ItemsChangedData *expected) +{ + g_assert_false (expected->called); + g_assert_cmpuint (expected->position, ==, position); + g_assert_cmpuint (expected->removed, ==, removed); + g_assert_cmpuint (expected->added, ==, added); + expected->called = TRUE; +} + +/* Test that all operations on the list emit the items-changed signal */ +static void +test_store_signal_items_changed (void) +{ + GListStore *store; + GListModel *model; + GSimpleAction *item; + struct ItemsChangedData expected = {0}; + + store = g_list_store_new (G_TYPE_SIMPLE_ACTION); + model = G_LIST_MODEL (store); + + g_object_connect (model, "signal::items-changed", + on_items_changed, &expected, NULL); + + /* Emit the signal manually */ + expect_items_changed (&expected, 0, 0, 0); + g_list_model_items_changed (model, 0, 0, 0); + g_assert_true (expected.called); + + /* Append an item */ + expect_items_changed (&expected, 0, 0, 1); + item = g_simple_action_new ("2", NULL); + g_list_store_append (store, item); + g_object_unref (item); + g_assert_true (expected.called); + + /* Insert an item */ + expect_items_changed (&expected, 1, 0, 1); + item = g_simple_action_new ("1", NULL); + g_list_store_insert (store, 1, item); + g_object_unref (item); + g_assert_true (expected.called); + + /* Sort the list */ + expect_items_changed (&expected, 0, 2, 2); + g_list_store_sort (store, + (GCompareDataFunc)list_model_cmp_action_by_name, + NULL); + g_assert_true (expected.called); + + /* Insert sorted */ + expect_items_changed (&expected, 2, 0, 1); + item = g_simple_action_new ("3", NULL); + g_list_store_insert_sorted (store, + item, + (GCompareDataFunc)list_model_cmp_action_by_name, + NULL); + g_object_unref (item); + g_assert_true (expected.called); + + /* Remove an item */ + expect_items_changed (&expected, 1, 1, 0); + g_list_store_remove (store, 1); + g_assert_true (expected.called); + + /* Splice */ + expect_items_changed (&expected, 0, 2, 1); + item = g_simple_action_new ("4", NULL); + g_assert_cmpuint (g_list_model_get_n_items (model), >=, 2); + g_list_store_splice (store, 0, 2, (gpointer)&item, 1); + g_object_unref (item); + g_assert_true (expected.called); + + /* Remove all */ + expect_items_changed (&expected, 0, 1, 0); + g_assert_cmpuint (g_list_model_get_n_items (model), ==, 1); + g_list_store_remove_all (store); + g_assert_true (expected.called); + + g_object_unref (store); +} + int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); @@ -330,6 +743,23 @@ int main (int argc, char *argv[]) test_store_splice_replace_middle); g_test_add_func ("/glistmodel/store/splice-replace-all", test_store_splice_replace_all); + g_test_add_func ("/glistmodel/store/splice-noop", test_store_splice_noop); + g_test_add_func ("/glistmodel/store/splice-remove-multiple", + test_store_splice_remove_multiple); + g_test_add_func ("/glistmodel/store/splice-add-multiple", + test_store_splice_add_multiple); + g_test_add_func ("/glistmodel/store/splice-wrong-type", + test_store_splice_wrong_type); + g_test_add_func ("/glistmodel/store/item-type", + test_store_item_type); + g_test_add_func ("/glistmodel/store/remove-all", + test_store_remove_all); + g_test_add_func ("/glistmodel/store/sort", + test_store_sort); + g_test_add_func ("/glistmodel/store/get-item-cache", + test_store_get_item_cache); + g_test_add_func ("/glistmodel/store/items-changed", + test_store_signal_items_changed); return g_test_run (); }