Branch data Line data Source code
1 : : /*
2 : : * Copyright 2015 Lars Uebernickel
3 : : *
4 : : * SPDX-License-Identifier: LGPL-2.1-or-later
5 : : *
6 : : * This library is free software; you can redistribute it and/or
7 : : * modify it under the terms of the GNU Lesser General Public
8 : : * License as published by the Free Software Foundation; either
9 : : * version 2.1 of the License, or (at your option) any later version.
10 : : *
11 : : * This library is distributed in the hope that it will be useful,
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : * Lesser General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Lesser General
17 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 : : *
19 : : * Authors: Lars Uebernickel <lars@uebernic.de>
20 : : */
21 : :
22 : : #include <gio/gio.h>
23 : :
24 : : #include <string.h>
25 : :
26 : : /* Wrapper around g_list_model_get_item() and g_list_model_get_object() which
27 : : * checks they return the same thing. */
28 : : static gpointer
29 : 3077 : list_model_get (GListModel *model,
30 : : guint position)
31 : : {
32 : 3077 : GObject *item = g_list_model_get_item (model, position);
33 : 3077 : GObject *object = g_list_model_get_object (model, position);
34 : :
35 : 3077 : g_assert_true (item == object);
36 : :
37 : 3077 : g_clear_object (&object);
38 : 3077 : return g_steal_pointer (&item);
39 : : }
40 : :
41 : : #define assert_cmpitems(store, cmp, n_items) G_STMT_START{ \
42 : : guint tmp; \
43 : : g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (store)), cmp, n_items); \
44 : : g_object_get (store, "n-items", &tmp, NULL); \
45 : : g_assert_cmpuint (tmp, cmp, n_items); \
46 : : }G_STMT_END
47 : :
48 : : /* Test that constructing/getting/setting properties on a #GListStore works. */
49 : : static void
50 : 1 : test_store_properties (void)
51 : : {
52 : 1 : GListStore *store = NULL;
53 : : GType item_type;
54 : :
55 : 1 : store = g_list_store_new (G_TYPE_MENU_ITEM);
56 : 1 : g_object_get (store, "item-type", &item_type, NULL);
57 : 1 : g_assert_cmpint (item_type, ==, G_TYPE_MENU_ITEM);
58 : :
59 : 1 : g_clear_object (&store);
60 : 1 : }
61 : :
62 : : /* Test that #GListStore rejects non-GObject item types. */
63 : : static void
64 : 1 : test_store_non_gobjects (void)
65 : : {
66 : 1 : if (g_test_subprocess ())
67 : : {
68 : : /* We have to use g_object_new() since g_list_store_new() checks the item
69 : : * type. We want to check the property setter code works properly. */
70 : 0 : g_object_new (G_TYPE_LIST_STORE, "item-type", G_TYPE_LONG, NULL);
71 : 0 : return;
72 : : }
73 : :
74 : 1 : g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_DEFAULT);
75 : 1 : g_test_trap_assert_failed ();
76 : 1 : g_test_trap_assert_stderr ("*CRITICAL*value * of type 'GType' is invalid or "
77 : : "out of range for property 'item-type'*");
78 : : }
79 : :
80 : : static void
81 : 1 : test_store_boundaries (void)
82 : : {
83 : : GListStore *store;
84 : : GMenuItem *item;
85 : :
86 : 1 : store = g_list_store_new (G_TYPE_MENU_ITEM);
87 : :
88 : 1 : item = g_menu_item_new (NULL, NULL);
89 : :
90 : : /* remove an item from an empty list */
91 : 1 : g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*g_sequence*");
92 : 1 : g_list_store_remove (store, 0);
93 : 1 : g_test_assert_expected_messages ();
94 : :
95 : : /* don't allow inserting an item past the end ... */
96 : 1 : g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*g_sequence*");
97 : 1 : g_list_store_insert (store, 1, item);
98 : 1 : assert_cmpitems (store, ==, 0);
99 : 1 : g_test_assert_expected_messages ();
100 : :
101 : : /* ... except exactly at the end */
102 : 1 : g_list_store_insert (store, 0, item);
103 : 1 : assert_cmpitems (store, ==, 1);
104 : :
105 : : /* remove a non-existing item at exactly the end of the list */
106 : 1 : g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*g_sequence*");
107 : 1 : g_list_store_remove (store, 1);
108 : 1 : g_test_assert_expected_messages ();
109 : :
110 : 1 : g_list_store_remove (store, 0);
111 : 1 : assert_cmpitems (store, ==, 0);
112 : :
113 : : /* splice beyond the end of the list */
114 : 1 : g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*position*");
115 : 1 : g_list_store_splice (store, 1, 0, NULL, 0);
116 : 1 : g_test_assert_expected_messages ();
117 : :
118 : : /* remove items from an empty list */
119 : 1 : g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*position*");
120 : 1 : g_list_store_splice (store, 0, 1, NULL, 0);
121 : 1 : g_test_assert_expected_messages ();
122 : :
123 : 1 : g_list_store_append (store, item);
124 : 1 : g_list_store_splice (store, 0, 1, (gpointer *) &item, 1);
125 : 1 : assert_cmpitems (store, ==, 1);
126 : :
127 : : /* remove more items than exist */
128 : 1 : g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*position*");
129 : 1 : g_list_store_splice (store, 0, 5, NULL, 0);
130 : 1 : g_test_assert_expected_messages ();
131 : 1 : assert_cmpitems (store, ==, 1);
132 : :
133 : 1 : g_object_unref (store);
134 : 1 : g_assert_finalize_object (item);
135 : 1 : }
136 : :
137 : : static void
138 : 1 : test_store_refcounts (void)
139 : : {
140 : : GListStore *store;
141 : : GMenuItem *items[10];
142 : : GMenuItem *tmp;
143 : : guint i;
144 : : guint n_items;
145 : :
146 : 1 : store = g_list_store_new (G_TYPE_MENU_ITEM);
147 : :
148 : 1 : assert_cmpitems (store, ==, 0);
149 : 1 : g_assert_null (list_model_get (G_LIST_MODEL (store), 0));
150 : :
151 : 1 : n_items = G_N_ELEMENTS (items);
152 : 11 : for (i = 0; i < n_items; i++)
153 : : {
154 : 10 : items[i] = g_menu_item_new (NULL, NULL);
155 : 10 : g_object_add_weak_pointer (G_OBJECT (items[i]), (gpointer *) &items[i]);
156 : 10 : g_list_store_append (store, items[i]);
157 : :
158 : 10 : g_object_unref (items[i]);
159 : 10 : g_assert_nonnull (items[i]);
160 : : }
161 : :
162 : 1 : assert_cmpitems (store, ==, n_items);
163 : 1 : g_assert_null (list_model_get (G_LIST_MODEL (store), n_items));
164 : :
165 : 1 : tmp = list_model_get (G_LIST_MODEL (store), 3);
166 : 1 : g_assert_true (tmp == items[3]);
167 : 1 : g_object_unref (tmp);
168 : :
169 : 1 : g_list_store_remove (store, 4);
170 : 1 : g_assert_null (items[4]);
171 : 1 : n_items--;
172 : 1 : assert_cmpitems (store, ==, n_items);
173 : 1 : g_assert_null (list_model_get (G_LIST_MODEL (store), n_items));
174 : :
175 : 1 : g_object_unref (store);
176 : 11 : for (i = 0; i < G_N_ELEMENTS (items); i++)
177 : 10 : g_assert_null (items[i]);
178 : 1 : }
179 : :
180 : : static gchar *
181 : 1000 : make_random_string (void)
182 : : {
183 : 1000 : gchar *str = g_malloc (10);
184 : : gint i;
185 : :
186 : 10000 : for (i = 0; i < 9; i++)
187 : 9000 : str[i] = g_test_rand_int_range ('a', 'z');
188 : 1000 : str[i] = '\0';
189 : :
190 : 1000 : return str;
191 : : }
192 : :
193 : : static gint
194 : 27581 : compare_items (gconstpointer a_p,
195 : : gconstpointer b_p,
196 : : gpointer user_data)
197 : : {
198 : 27581 : GObject *a_o = (GObject *) a_p;
199 : 27581 : GObject *b_o = (GObject *) b_p;
200 : :
201 : 27581 : gchar *a = g_object_get_data (a_o, "key");
202 : 27581 : gchar *b = g_object_get_data (b_o, "key");
203 : :
204 : 27581 : g_assert (user_data == GUINT_TO_POINTER(0x1234u));
205 : :
206 : 27581 : return strcmp (a, b);
207 : : }
208 : :
209 : : static void
210 : 2000 : insert_string (GListStore *store,
211 : : const gchar *str)
212 : : {
213 : : GObject *obj;
214 : :
215 : 2000 : obj = g_object_new (G_TYPE_OBJECT, NULL);
216 : 2000 : g_object_set_data_full (obj, "key", g_strdup (str), g_free);
217 : :
218 : 2000 : g_list_store_insert_sorted (store, obj, compare_items, GUINT_TO_POINTER(0x1234u));
219 : :
220 : 2000 : g_object_unref (obj);
221 : 2000 : }
222 : :
223 : : static void
224 : 1 : test_store_sorted (void)
225 : : {
226 : : GListStore *store;
227 : : guint i;
228 : :
229 : 1 : store = g_list_store_new (G_TYPE_OBJECT);
230 : :
231 : 1001 : for (i = 0; i < 1000; i++)
232 : : {
233 : 1000 : gchar *str = make_random_string ();
234 : 1000 : insert_string (store, str);
235 : 1000 : insert_string (store, str); /* multiple copies of the same are OK */
236 : 1000 : g_free (str);
237 : : }
238 : :
239 : 1 : assert_cmpitems (store, ==, 2000);
240 : :
241 : 1001 : for (i = 0; i < 1000; i++)
242 : : {
243 : : GObject *a, *b;
244 : :
245 : : /* should see our two copies */
246 : 1000 : a = list_model_get (G_LIST_MODEL (store), i * 2);
247 : 1000 : b = list_model_get (G_LIST_MODEL (store), i * 2 + 1);
248 : :
249 : 1000 : g_assert (compare_items (a, b, GUINT_TO_POINTER(0x1234)) == 0);
250 : 1000 : g_assert (a != b);
251 : :
252 : 1000 : if (i)
253 : : {
254 : : GObject *c;
255 : :
256 : 999 : c = list_model_get (G_LIST_MODEL (store), i * 2 - 1);
257 : 999 : g_assert (c != a);
258 : 999 : g_assert (c != b);
259 : :
260 : 999 : g_assert (compare_items (b, c, GUINT_TO_POINTER(0x1234)) > 0);
261 : 999 : g_assert (compare_items (a, c, GUINT_TO_POINTER(0x1234)) > 0);
262 : :
263 : 999 : g_object_unref (c);
264 : : }
265 : :
266 : 1000 : g_object_unref (a);
267 : 1000 : g_object_unref (b);
268 : : }
269 : :
270 : 1 : g_object_unref (store);
271 : 1 : }
272 : :
273 : : /* Test that using splice() to replace the middle element in a list store works. */
274 : : static void
275 : 1 : test_store_splice_replace_middle (void)
276 : : {
277 : : GListStore *store;
278 : : GListModel *model;
279 : : GAction *item;
280 : : GPtrArray *array;
281 : :
282 : 1 : g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=795307");
283 : :
284 : 1 : store = g_list_store_new (G_TYPE_SIMPLE_ACTION);
285 : 1 : model = G_LIST_MODEL (store);
286 : :
287 : 1 : array = g_ptr_array_new_full (0, g_object_unref);
288 : 1 : g_ptr_array_add (array, g_simple_action_new ("1", NULL));
289 : 1 : g_ptr_array_add (array, g_simple_action_new ("2", NULL));
290 : 1 : g_ptr_array_add (array, g_simple_action_new ("3", NULL));
291 : 1 : g_ptr_array_add (array, g_simple_action_new ("4", NULL));
292 : 1 : g_ptr_array_add (array, g_simple_action_new ("5", NULL));
293 : :
294 : : /* Add three items through splice */
295 : 1 : g_list_store_splice (store, 0, 0, array->pdata, 3);
296 : 1 : assert_cmpitems (store, ==, 3);
297 : :
298 : 1 : item = list_model_get (model, 0);
299 : 1 : g_assert_cmpstr (g_action_get_name (item), ==, "1");
300 : 1 : g_object_unref (item);
301 : 1 : item = list_model_get (model, 1);
302 : 1 : g_assert_cmpstr (g_action_get_name (item), ==, "2");
303 : 1 : g_object_unref (item);
304 : 1 : item = list_model_get (model, 2);
305 : 1 : g_assert_cmpstr (g_action_get_name (item), ==, "3");
306 : 1 : g_object_unref (item);
307 : :
308 : : /* Replace the middle one with two new items */
309 : 1 : g_list_store_splice (store, 1, 1, array->pdata + 3, 2);
310 : 1 : assert_cmpitems (store, ==, 4);
311 : :
312 : 1 : item = list_model_get (model, 0);
313 : 1 : g_assert_cmpstr (g_action_get_name (item), ==, "1");
314 : 1 : g_object_unref (item);
315 : 1 : item = list_model_get (model, 1);
316 : 1 : g_assert_cmpstr (g_action_get_name (item), ==, "4");
317 : 1 : g_object_unref (item);
318 : 1 : item = list_model_get (model, 2);
319 : 1 : g_assert_cmpstr (g_action_get_name (item), ==, "5");
320 : 1 : g_object_unref (item);
321 : 1 : item = list_model_get (model, 3);
322 : 1 : g_assert_cmpstr (g_action_get_name (item), ==, "3");
323 : 1 : g_object_unref (item);
324 : :
325 : 1 : g_ptr_array_unref (array);
326 : 1 : g_object_unref (store);
327 : 1 : }
328 : :
329 : : /* Test that using splice() to replace the whole list store works. */
330 : : static void
331 : 1 : test_store_splice_replace_all (void)
332 : : {
333 : : GListStore *store;
334 : : GListModel *model;
335 : : GPtrArray *array;
336 : : GAction *item;
337 : :
338 : 1 : g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=795307");
339 : :
340 : 1 : store = g_list_store_new (G_TYPE_SIMPLE_ACTION);
341 : 1 : model = G_LIST_MODEL (store);
342 : :
343 : 1 : array = g_ptr_array_new_full (0, g_object_unref);
344 : 1 : g_ptr_array_add (array, g_simple_action_new ("1", NULL));
345 : 1 : g_ptr_array_add (array, g_simple_action_new ("2", NULL));
346 : 1 : g_ptr_array_add (array, g_simple_action_new ("3", NULL));
347 : 1 : g_ptr_array_add (array, g_simple_action_new ("4", NULL));
348 : :
349 : : /* Add the first two */
350 : 1 : g_list_store_splice (store, 0, 0, array->pdata, 2);
351 : :
352 : 1 : assert_cmpitems (store, ==, 2);
353 : 1 : item = list_model_get (model, 0);
354 : 1 : g_assert_cmpstr (g_action_get_name (item), ==, "1");
355 : 1 : g_object_unref (item);
356 : 1 : item = list_model_get (model, 1);
357 : 1 : g_assert_cmpstr (g_action_get_name (item), ==, "2");
358 : 1 : g_object_unref (item);
359 : :
360 : : /* Replace all with the last two */
361 : 1 : g_list_store_splice (store, 0, 2, array->pdata + 2, 2);
362 : :
363 : 1 : assert_cmpitems (store, ==, 2);
364 : 1 : item = list_model_get (model, 0);
365 : 1 : g_assert_cmpstr (g_action_get_name (item), ==, "3");
366 : 1 : g_object_unref (item);
367 : 1 : item = list_model_get (model, 1);
368 : 1 : g_assert_cmpstr (g_action_get_name (item), ==, "4");
369 : 1 : g_object_unref (item);
370 : :
371 : 1 : g_ptr_array_unref (array);
372 : 1 : g_object_unref (store);
373 : 1 : }
374 : :
375 : : /* Test that using splice() without removing or adding anything works */
376 : : static void
377 : 1 : test_store_splice_noop (void)
378 : : {
379 : : GListStore *store;
380 : : GListModel *model;
381 : : GAction *item;
382 : :
383 : 1 : store = g_list_store_new (G_TYPE_SIMPLE_ACTION);
384 : 1 : model = G_LIST_MODEL (store);
385 : :
386 : : /* splice noop with an empty list */
387 : 1 : g_list_store_splice (store, 0, 0, NULL, 0);
388 : 1 : assert_cmpitems (store, ==, 0);
389 : :
390 : : /* splice noop with a non-empty list */
391 : 1 : item = G_ACTION (g_simple_action_new ("1", NULL));
392 : 1 : g_list_store_append (store, item);
393 : 1 : g_object_unref (item);
394 : :
395 : 1 : g_list_store_splice (store, 0, 0, NULL, 0);
396 : 1 : assert_cmpitems (store, ==, 1);
397 : :
398 : 1 : g_list_store_splice (store, 1, 0, NULL, 0);
399 : 1 : assert_cmpitems (store, ==, 1);
400 : :
401 : 1 : item = list_model_get (model, 0);
402 : 1 : g_assert_cmpstr (g_action_get_name (item), ==, "1");
403 : 1 : g_object_unref (item);
404 : :
405 : 1 : g_object_unref (store);
406 : 1 : }
407 : :
408 : : static gboolean
409 : 11 : model_array_equal (GListModel *model, GPtrArray *array)
410 : : {
411 : : guint i;
412 : :
413 : 11 : if (g_list_model_get_n_items (model) != array->len)
414 : 3 : return FALSE;
415 : :
416 : 60 : for (i = 0; i < array->len; i++)
417 : : {
418 : : GObject *ptr;
419 : : gboolean ptrs_equal;
420 : :
421 : 53 : ptr = list_model_get (model, i);
422 : 53 : ptrs_equal = (g_ptr_array_index (array, i) == ptr);
423 : 53 : g_object_unref (ptr);
424 : 53 : if (!ptrs_equal)
425 : 1 : return FALSE;
426 : : }
427 : :
428 : 7 : return TRUE;
429 : : }
430 : :
431 : : /* Test that using splice() to remove multiple items at different
432 : : * positions works */
433 : : static void
434 : 1 : test_store_splice_remove_multiple (void)
435 : : {
436 : : GListStore *store;
437 : : GListModel *model;
438 : : GPtrArray *array;
439 : :
440 : 1 : store = g_list_store_new (G_TYPE_SIMPLE_ACTION);
441 : 1 : model = G_LIST_MODEL (store);
442 : :
443 : 1 : array = g_ptr_array_new_full (0, g_object_unref);
444 : 1 : g_ptr_array_add (array, g_simple_action_new ("1", NULL));
445 : 1 : g_ptr_array_add (array, g_simple_action_new ("2", NULL));
446 : 1 : g_ptr_array_add (array, g_simple_action_new ("3", NULL));
447 : 1 : g_ptr_array_add (array, g_simple_action_new ("4", NULL));
448 : 1 : g_ptr_array_add (array, g_simple_action_new ("5", NULL));
449 : 1 : g_ptr_array_add (array, g_simple_action_new ("6", NULL));
450 : 1 : g_ptr_array_add (array, g_simple_action_new ("7", NULL));
451 : 1 : g_ptr_array_add (array, g_simple_action_new ("8", NULL));
452 : 1 : g_ptr_array_add (array, g_simple_action_new ("9", NULL));
453 : 1 : g_ptr_array_add (array, g_simple_action_new ("10", NULL));
454 : :
455 : : /* Add all */
456 : 1 : g_list_store_splice (store, 0, 0, array->pdata, array->len);
457 : 1 : g_assert_true (model_array_equal (model, array));
458 : :
459 : : /* Remove the first two */
460 : 1 : g_list_store_splice (store, 0, 2, NULL, 0);
461 : 1 : g_assert_false (model_array_equal (model, array));
462 : 1 : g_ptr_array_remove_range (array, 0, 2);
463 : 1 : g_assert_true (model_array_equal (model, array));
464 : 1 : assert_cmpitems (store, ==, 8);
465 : :
466 : : /* Remove two in the middle */
467 : 1 : g_list_store_splice (store, 2, 2, NULL, 0);
468 : 1 : g_assert_false (model_array_equal (model, array));
469 : 1 : g_ptr_array_remove_range (array, 2, 2);
470 : 1 : g_assert_true (model_array_equal (model, array));
471 : 1 : assert_cmpitems (store, ==, 6);
472 : :
473 : : /* Remove two at the end */
474 : 1 : g_list_store_splice (store, 4, 2, NULL, 0);
475 : 1 : g_assert_false (model_array_equal (model, array));
476 : 1 : g_ptr_array_remove_range (array, 4, 2);
477 : 1 : g_assert_true (model_array_equal (model, array));
478 : 1 : assert_cmpitems (store, ==, 4);
479 : :
480 : 1 : g_ptr_array_unref (array);
481 : 1 : g_object_unref (store);
482 : 1 : }
483 : :
484 : : /* Test that using splice() to add multiple items at different
485 : : * positions works */
486 : : static void
487 : 1 : test_store_splice_add_multiple (void)
488 : : {
489 : : GListStore *store;
490 : : GListModel *model;
491 : : GPtrArray *array;
492 : :
493 : 1 : store = g_list_store_new (G_TYPE_SIMPLE_ACTION);
494 : 1 : model = G_LIST_MODEL (store);
495 : :
496 : 1 : array = g_ptr_array_new_full (0, g_object_unref);
497 : 1 : g_ptr_array_add (array, g_simple_action_new ("1", NULL));
498 : 1 : g_ptr_array_add (array, g_simple_action_new ("2", NULL));
499 : 1 : g_ptr_array_add (array, g_simple_action_new ("3", NULL));
500 : 1 : g_ptr_array_add (array, g_simple_action_new ("4", NULL));
501 : 1 : g_ptr_array_add (array, g_simple_action_new ("5", NULL));
502 : 1 : g_ptr_array_add (array, g_simple_action_new ("6", NULL));
503 : :
504 : : /* Add two at the beginning */
505 : 1 : g_list_store_splice (store, 0, 0, array->pdata, 2);
506 : :
507 : : /* Add two at the end */
508 : 1 : g_list_store_splice (store, 2, 0, array->pdata + 4, 2);
509 : :
510 : : /* Add two in the middle */
511 : 1 : g_list_store_splice (store, 2, 0, array->pdata + 2, 2);
512 : :
513 : 1 : g_assert_true (model_array_equal (model, array));
514 : :
515 : 1 : g_ptr_array_unref (array);
516 : 1 : g_object_unref (store);
517 : 1 : }
518 : :
519 : : /* Test that get_item_type() returns the right type */
520 : : static void
521 : 1 : test_store_item_type (void)
522 : : {
523 : : GListStore *store;
524 : : GType gtype;
525 : :
526 : 1 : store = g_list_store_new (G_TYPE_SIMPLE_ACTION);
527 : 1 : gtype = g_list_model_get_item_type (G_LIST_MODEL (store));
528 : 1 : g_assert (gtype == G_TYPE_SIMPLE_ACTION);
529 : :
530 : 1 : g_object_unref (store);
531 : 1 : }
532 : :
533 : : /* Test that remove_all() removes all items */
534 : : static void
535 : 1 : test_store_remove_all (void)
536 : : {
537 : : GListStore *store;
538 : : GSimpleAction *item;
539 : :
540 : 1 : store = g_list_store_new (G_TYPE_SIMPLE_ACTION);
541 : :
542 : : /* Test with an empty list */
543 : 1 : g_list_store_remove_all (store);
544 : 1 : assert_cmpitems (store, ==, 0);
545 : :
546 : : /* Test with a non-empty list */
547 : 1 : item = g_simple_action_new ("42", NULL);
548 : 1 : g_list_store_append (store, item);
549 : 1 : g_list_store_append (store, item);
550 : 1 : g_object_unref (item);
551 : 1 : assert_cmpitems (store, ==, 2);
552 : 1 : g_list_store_remove_all (store);
553 : 1 : assert_cmpitems (store, ==, 0);
554 : :
555 : 1 : g_object_unref (store);
556 : 1 : }
557 : :
558 : : /* Test that splice() logs an error when passed the wrong item type */
559 : : static void
560 : 1 : test_store_splice_wrong_type (void)
561 : : {
562 : : GListStore *store;
563 : :
564 : 1 : store = g_list_store_new (G_TYPE_SIMPLE_ACTION);
565 : :
566 : 1 : g_test_expect_message (G_LOG_DOMAIN,
567 : : G_LOG_LEVEL_CRITICAL,
568 : : "*GListStore instead of a GSimpleAction*");
569 : 1 : g_list_store_splice (store, 0, 0, (gpointer)&store, 1);
570 : :
571 : 1 : g_object_unref (store);
572 : 1 : }
573 : :
574 : : static gint
575 : 42 : cmp_action_by_name (GAction *a, GAction *b, gpointer user_data)
576 : : {
577 : 42 : return g_strcmp0 (g_action_get_name (a), g_action_get_name (b));
578 : : }
579 : :
580 : : /* Test if sort() works */
581 : : static void
582 : 1 : test_store_sort (void)
583 : : {
584 : : GListStore *store;
585 : : GListModel *model;
586 : : GPtrArray *array;
587 : :
588 : 1 : store = g_list_store_new (G_TYPE_SIMPLE_ACTION);
589 : 1 : model = G_LIST_MODEL (store);
590 : :
591 : 1 : array = g_ptr_array_new_full (0, g_object_unref);
592 : 1 : g_ptr_array_add (array, g_simple_action_new ("2", NULL));
593 : 1 : g_ptr_array_add (array, g_simple_action_new ("3", NULL));
594 : 1 : g_ptr_array_add (array, g_simple_action_new ("9", NULL));
595 : 1 : g_ptr_array_add (array, g_simple_action_new ("4", NULL));
596 : 1 : g_ptr_array_add (array, g_simple_action_new ("5", NULL));
597 : 1 : g_ptr_array_add (array, g_simple_action_new ("8", NULL));
598 : 1 : g_ptr_array_add (array, g_simple_action_new ("6", NULL));
599 : 1 : g_ptr_array_add (array, g_simple_action_new ("7", NULL));
600 : 1 : g_ptr_array_add (array, g_simple_action_new ("1", NULL));
601 : :
602 : : /* Sort an empty list */
603 : 1 : g_list_store_sort (store, (GCompareDataFunc) cmp_action_by_name, NULL);
604 : :
605 : : /* Add all */
606 : 1 : g_list_store_splice (store, 0, 0, array->pdata, array->len);
607 : 1 : g_assert_true (model_array_equal (model, array));
608 : :
609 : : /* Sort both and check if the result is the same */
610 : 1 : g_ptr_array_sort_values (array, (GCompareFunc)cmp_action_by_name);
611 : 1 : g_assert_false (model_array_equal (model, array));
612 : 1 : g_list_store_sort (store, (GCompareDataFunc) cmp_action_by_name, NULL);
613 : 1 : g_assert_true (model_array_equal (model, array));
614 : :
615 : 1 : g_ptr_array_unref (array);
616 : 1 : g_object_unref (store);
617 : 1 : }
618 : :
619 : : /* Test the cases where the item store tries to speed up item access by caching
620 : : * the last iter/position */
621 : : static void
622 : 1 : test_store_get_item_cache (void)
623 : : {
624 : : GListStore *store;
625 : : GListModel *model;
626 : : GSimpleAction *item1, *item2, *temp;
627 : :
628 : 1 : store = g_list_store_new (G_TYPE_SIMPLE_ACTION);
629 : 1 : model = G_LIST_MODEL (store);
630 : :
631 : : /* Add two */
632 : 1 : item1 = g_simple_action_new ("1", NULL);
633 : 1 : g_list_store_append (store, item1);
634 : 1 : item2 = g_simple_action_new ("2", NULL);
635 : 1 : g_list_store_append (store, item2);
636 : :
637 : : /* Clear the cache */
638 : 1 : g_assert_null (list_model_get (model, 42));
639 : :
640 : : /* Access the same position twice */
641 : 1 : temp = list_model_get (model, 1);
642 : 1 : g_assert (temp == item2);
643 : 1 : g_object_unref (temp);
644 : 1 : temp = list_model_get (model, 1);
645 : 1 : g_assert (temp == item2);
646 : 1 : g_object_unref (temp);
647 : :
648 : 1 : g_assert_null (list_model_get (model, 42));
649 : :
650 : : /* Access forwards */
651 : 1 : temp = list_model_get (model, 0);
652 : 1 : g_assert (temp == item1);
653 : 1 : g_object_unref (temp);
654 : 1 : temp = list_model_get (model, 1);
655 : 1 : g_assert (temp == item2);
656 : 1 : g_object_unref (temp);
657 : :
658 : 1 : g_assert_null (list_model_get (model, 42));
659 : :
660 : : /* Access backwards */
661 : 1 : temp = list_model_get (model, 1);
662 : 1 : g_assert (temp == item2);
663 : 1 : g_object_unref (temp);
664 : 1 : temp = list_model_get (model, 0);
665 : 1 : g_assert (temp == item1);
666 : 1 : g_object_unref (temp);
667 : :
668 : 1 : g_object_unref (item1);
669 : 1 : g_object_unref (item2);
670 : 1 : g_object_unref (store);
671 : 1 : }
672 : :
673 : : struct ItemsChangedData
674 : : {
675 : : guint position;
676 : : guint removed;
677 : : guint added;
678 : : gboolean called;
679 : : gboolean notified;
680 : : };
681 : :
682 : : static void
683 : 9 : expect_items_changed (struct ItemsChangedData *expected,
684 : : guint position,
685 : : guint removed,
686 : : guint added)
687 : : {
688 : 9 : expected->position = position;
689 : 9 : expected->removed = removed;
690 : 9 : expected->added = added;
691 : 9 : expected->called = FALSE;
692 : 9 : expected->notified = FALSE;
693 : 9 : }
694 : :
695 : : static void
696 : 9 : on_items_changed (GListModel *model,
697 : : guint position,
698 : : guint removed,
699 : : guint added,
700 : : struct ItemsChangedData *expected)
701 : : {
702 : 9 : g_assert_false (expected->called);
703 : 9 : g_assert_cmpuint (expected->position, ==, position);
704 : 9 : g_assert_cmpuint (expected->removed, ==, removed);
705 : 9 : g_assert_cmpuint (expected->added, ==, added);
706 : 9 : expected->called = TRUE;
707 : 9 : }
708 : :
709 : : static void
710 : 6 : on_notify_n_items (GListModel *model,
711 : : GParamSpec *pspec,
712 : : struct ItemsChangedData *expected)
713 : : {
714 : 6 : g_assert_false (expected->notified);
715 : 6 : expected->notified = TRUE;
716 : 6 : }
717 : :
718 : : /* Test that all operations on the list emit the items-changed signal */
719 : : static void
720 : 1 : test_store_signal_items_changed (void)
721 : : {
722 : : GListStore *store;
723 : : GListModel *model;
724 : : GSimpleAction *item;
725 : 1 : struct ItemsChangedData expected = {0};
726 : :
727 : 1 : store = g_list_store_new (G_TYPE_SIMPLE_ACTION);
728 : 1 : model = G_LIST_MODEL (store);
729 : :
730 : 1 : g_object_connect (model, "signal::items-changed",
731 : : on_items_changed, &expected, NULL);
732 : 1 : g_object_connect (model, "signal::notify::n-items",
733 : : on_notify_n_items, &expected, NULL);
734 : :
735 : : /* Emit the signal manually */
736 : 1 : expect_items_changed (&expected, 0, 0, 0);
737 : 1 : g_list_model_items_changed (model, 0, 0, 0);
738 : 1 : g_assert_true (expected.called);
739 : 1 : g_assert_false (expected.notified);
740 : :
741 : : /* Append an item */
742 : 1 : expect_items_changed (&expected, 0, 0, 1);
743 : 1 : item = g_simple_action_new ("2", NULL);
744 : 1 : g_list_store_append (store, item);
745 : 1 : g_object_unref (item);
746 : 1 : g_assert_true (expected.called);
747 : 1 : g_assert_true (expected.notified);
748 : :
749 : : /* Insert an item */
750 : 1 : expect_items_changed (&expected, 1, 0, 1);
751 : 1 : item = g_simple_action_new ("1", NULL);
752 : 1 : g_list_store_insert (store, 1, item);
753 : 1 : g_object_unref (item);
754 : 1 : g_assert_true (expected.called);
755 : 1 : g_assert_true (expected.notified);
756 : :
757 : : /* Sort the list */
758 : 1 : expect_items_changed (&expected, 0, 2, 2);
759 : 1 : g_list_store_sort (store,
760 : : (GCompareDataFunc) cmp_action_by_name,
761 : : NULL);
762 : 1 : g_assert_true (expected.called);
763 : 1 : g_assert_false (expected.notified);
764 : :
765 : : /* Insert sorted */
766 : 1 : expect_items_changed (&expected, 2, 0, 1);
767 : 1 : item = g_simple_action_new ("3", NULL);
768 : 1 : g_list_store_insert_sorted (store,
769 : : item,
770 : : (GCompareDataFunc) cmp_action_by_name,
771 : : NULL);
772 : 1 : g_object_unref (item);
773 : 1 : g_assert_true (expected.called);
774 : 1 : g_assert_true (expected.notified);
775 : :
776 : : /* Remove an item */
777 : 1 : expect_items_changed (&expected, 1, 1, 0);
778 : 1 : g_list_store_remove (store, 1);
779 : 1 : g_assert_true (expected.called);
780 : 1 : g_assert_true (expected.notified);
781 : :
782 : : /* Splice */
783 : 1 : expect_items_changed (&expected, 0, 2, 1);
784 : 1 : item = g_simple_action_new ("4", NULL);
785 : 1 : assert_cmpitems (store, >=, 2);
786 : 1 : g_list_store_splice (store, 0, 2, (gpointer)&item, 1);
787 : 1 : g_object_unref (item);
788 : 1 : g_assert_true (expected.called);
789 : 1 : g_assert_true (expected.notified);
790 : :
791 : : /* Splice to replace */
792 : 1 : expect_items_changed (&expected, 0, 1, 1);
793 : 1 : item = g_simple_action_new ("5", NULL);
794 : 1 : assert_cmpitems (store, >=, 1);
795 : 1 : g_list_store_splice (store, 0, 1, (gpointer)&item, 1);
796 : 1 : g_object_unref (item);
797 : 1 : g_assert_true (expected.called);
798 : 1 : g_assert_false (expected.notified);
799 : :
800 : : /* Remove all */
801 : 1 : expect_items_changed (&expected, 0, 1, 0);
802 : 1 : assert_cmpitems (store, ==, 1);
803 : 1 : g_list_store_remove_all (store);
804 : 1 : g_assert_true (expected.called);
805 : 1 : g_assert_true (expected.notified);
806 : :
807 : 1 : g_object_unref (store);
808 : 1 : }
809 : :
810 : : /* Due to an overflow in the list store last-iter optimization,
811 : : * the sequence 'lookup 0; lookup MAXUINT' was returning the
812 : : * same item twice, and not NULL for the second lookup.
813 : : * See #1639.
814 : : */
815 : : static void
816 : 1 : test_store_past_end (void)
817 : : {
818 : : GListStore *store;
819 : : GListModel *model;
820 : : GSimpleAction *item;
821 : :
822 : 1 : store = g_list_store_new (G_TYPE_SIMPLE_ACTION);
823 : 1 : model = G_LIST_MODEL (store);
824 : :
825 : 1 : item = g_simple_action_new ("2", NULL);
826 : 1 : g_list_store_append (store, item);
827 : 1 : g_object_unref (item);
828 : :
829 : 1 : assert_cmpitems (store, ==, 1);
830 : 1 : item = g_list_model_get_item (model, 0);
831 : 1 : g_assert_nonnull (item);
832 : 1 : g_object_unref (item);
833 : 1 : item = g_list_model_get_item (model, G_MAXUINT);
834 : 1 : g_assert_null (item);
835 : :
836 : 1 : g_object_unref (store);
837 : 1 : }
838 : :
839 : : static gboolean
840 : 3 : list_model_casecmp_action_by_name (gconstpointer a,
841 : : gconstpointer b)
842 : : {
843 : 3 : return g_ascii_strcasecmp (g_action_get_name (G_ACTION (a)),
844 : 6 : g_action_get_name (G_ACTION (b))) == 0;
845 : : }
846 : :
847 : : static gboolean
848 : 4 : list_model_casecmp_action_by_name_full (gconstpointer a,
849 : : gconstpointer b,
850 : : gpointer user_data)
851 : : {
852 : : char buf[4];
853 : 4 : const char *suffix = user_data;
854 : :
855 : 4 : g_snprintf (buf, sizeof buf, "%s%s", g_action_get_name (G_ACTION (b)), suffix);
856 : 4 : return g_strcmp0 (g_action_get_name (G_ACTION (a)), buf) == 0;
857 : : }
858 : :
859 : : /* Test if find() and find_with_equal_func() works */
860 : : static void
861 : 1 : test_store_find (void)
862 : : {
863 : : GListStore *store;
864 : 1 : guint position = 100;
865 : 1 : const gchar *item_strs[4] = { "aaa", "bbb", "xxx", "ccc" };
866 : 1 : GSimpleAction *items[4] = { NULL, };
867 : : GSimpleAction *other_item;
868 : : guint i;
869 : :
870 : 1 : store = g_list_store_new (G_TYPE_SIMPLE_ACTION);
871 : :
872 : 5 : for (i = 0; i < G_N_ELEMENTS (item_strs); i++)
873 : 4 : items[i] = g_simple_action_new (item_strs[i], NULL);
874 : :
875 : : /* Shouldn't crash on an empty list, or change the position pointer */
876 : 1 : g_assert_false (g_list_store_find (store, items[0], NULL));
877 : 1 : g_assert_false (g_list_store_find (store, items[0], &position));
878 : 1 : g_assert_cmpint (position, ==, 100);
879 : :
880 : 5 : for (i = 0; i < G_N_ELEMENTS (item_strs); i++)
881 : 4 : g_list_store_append (store, items[i]);
882 : :
883 : : /* Check whether it could still find the the elements */
884 : 5 : for (i = 0; i < G_N_ELEMENTS (item_strs); i++)
885 : : {
886 : 4 : g_assert_true (g_list_store_find (store, items[i], &position));
887 : 4 : g_assert_cmpint (position, ==, i);
888 : : /* Shouldn't try to write to position pointer if NULL given */
889 : 4 : g_assert_true (g_list_store_find (store, items[i], NULL));
890 : : }
891 : :
892 : : /* try to find element not part of the list */
893 : 1 : other_item = g_simple_action_new ("111", NULL);
894 : 1 : g_assert_false (g_list_store_find (store, other_item, NULL));
895 : 1 : g_clear_object (&other_item);
896 : :
897 : : /* Re-add item; find() should only return the first position */
898 : 1 : g_list_store_append (store, items[0]);
899 : 1 : g_assert_true (g_list_store_find (store, items[0], &position));
900 : 1 : g_assert_cmpint (position, ==, 0);
901 : :
902 : : /* try to find element which should only work with custom equality check */
903 : 1 : other_item = g_simple_action_new ("XXX", NULL);
904 : 1 : g_assert_false (g_list_store_find (store, other_item, NULL));
905 : 1 : g_assert_true (g_list_store_find_with_equal_func (store,
906 : : other_item,
907 : : list_model_casecmp_action_by_name,
908 : : &position));
909 : 1 : g_assert_cmpint (position, ==, 2);
910 : 1 : g_clear_object (&other_item);
911 : :
912 : : /* try to find element which should only work with custom equality check and string concat */
913 : 1 : other_item = g_simple_action_new ("c", NULL);
914 : 1 : g_assert_false (g_list_store_find (store, other_item, NULL));
915 : 1 : g_assert_true (g_list_store_find_with_equal_func_full (store,
916 : : other_item,
917 : : list_model_casecmp_action_by_name_full,
918 : : "cc",
919 : : &position));
920 : 1 : g_assert_cmpint (position, ==, 3);
921 : 1 : g_clear_object (&other_item);
922 : :
923 : 5 : for (i = 0; i < G_N_ELEMENTS (item_strs); i++)
924 : 4 : g_clear_object(&items[i]);
925 : 1 : g_clear_object (&store);
926 : 1 : }
927 : :
928 : 1 : int main (int argc, char *argv[])
929 : : {
930 : 1 : g_test_init (&argc, &argv, NULL);
931 : :
932 : 1 : g_test_add_func ("/glistmodel/store/properties", test_store_properties);
933 : 1 : g_test_add_func ("/glistmodel/store/non-gobjects", test_store_non_gobjects);
934 : 1 : g_test_add_func ("/glistmodel/store/boundaries", test_store_boundaries);
935 : 1 : g_test_add_func ("/glistmodel/store/refcounts", test_store_refcounts);
936 : 1 : g_test_add_func ("/glistmodel/store/sorted", test_store_sorted);
937 : 1 : g_test_add_func ("/glistmodel/store/splice-replace-middle",
938 : : test_store_splice_replace_middle);
939 : 1 : g_test_add_func ("/glistmodel/store/splice-replace-all",
940 : : test_store_splice_replace_all);
941 : 1 : g_test_add_func ("/glistmodel/store/splice-noop", test_store_splice_noop);
942 : 1 : g_test_add_func ("/glistmodel/store/splice-remove-multiple",
943 : : test_store_splice_remove_multiple);
944 : 1 : g_test_add_func ("/glistmodel/store/splice-add-multiple",
945 : : test_store_splice_add_multiple);
946 : 1 : g_test_add_func ("/glistmodel/store/splice-wrong-type",
947 : : test_store_splice_wrong_type);
948 : 1 : g_test_add_func ("/glistmodel/store/item-type",
949 : : test_store_item_type);
950 : 1 : g_test_add_func ("/glistmodel/store/remove-all",
951 : : test_store_remove_all);
952 : 1 : g_test_add_func ("/glistmodel/store/sort",
953 : : test_store_sort);
954 : 1 : g_test_add_func ("/glistmodel/store/get-item-cache",
955 : : test_store_get_item_cache);
956 : 1 : g_test_add_func ("/glistmodel/store/items-changed",
957 : : test_store_signal_items_changed);
958 : 1 : g_test_add_func ("/glistmodel/store/past-end", test_store_past_end);
959 : 1 : g_test_add_func ("/glistmodel/store/find", test_store_find);
960 : :
961 : 1 : return g_test_run ();
962 : : }
|