1/* GtkRBTree tests.
2 *
3 * Copyright (C) 2011, Red Hat, Inc.
4 * Authors: Benjamin Otte <otte@gnome.org>
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 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 Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <locale.h>
21
22#include <gtk/gtk.h>
23
24static GQuark number_quark;
25static GQuark changes_quark;
26
27static guint
28get (GListModel *model,
29 guint position)
30{
31 GObject *object = g_list_model_get_item (list: model, position);
32 guint number;
33 g_assert_nonnull (object);
34 number = GPOINTER_TO_UINT (g_object_get_qdata (object, number_quark));
35 g_object_unref (object);
36 return number;
37}
38
39static char *
40model_to_string (GListModel *model)
41{
42 GString *string = g_string_new (NULL);
43 guint i;
44
45 for (i = 0; i < g_list_model_get_n_items (list: model); i++)
46 {
47 if (i > 0)
48 g_string_append (string, val: " ");
49 g_string_append_printf (string, format: "%u", get (model, position: i));
50 }
51
52 return g_string_free (string, FALSE);
53}
54
55static GListStore *
56new_store (guint start,
57 guint end,
58 guint step);
59
60static void
61splice (GListStore *store,
62 guint pos,
63 guint removed,
64 guint *numbers,
65 guint added)
66{
67 GObject **objects = g_newa (GObject *, added);
68 guint i;
69
70 for (i = 0; i < added; i++)
71 {
72 /* 0 cannot be differentiated from NULL, so don't use it */
73 g_assert_cmpint (numbers[i], !=, 0);
74 objects[i] = g_object_new (G_TYPE_OBJECT, NULL);
75 g_object_set_qdata (object: objects[i], quark: number_quark, GUINT_TO_POINTER (numbers[i]));
76 }
77
78 g_list_store_splice (store, position: pos, n_removals: removed, additions: (gpointer *) objects, n_additions: added);
79
80 for (i = 0; i < added; i++)
81 g_object_unref (object: objects[i]);
82}
83
84static void
85insert (GListStore *store,
86 guint pos,
87 guint number)
88{
89 GObject *object;
90
91 /* 0 cannot be differentiated from NULL, so don't use it */
92 g_assert_cmpint (number, !=, 0);
93
94 object = g_object_new (G_TYPE_OBJECT, NULL);
95 g_object_set_qdata (object, quark: number_quark, GUINT_TO_POINTER (number));
96 g_list_store_insert (store, position: pos, item: object);
97 g_object_unref (object);
98}
99
100static void
101add (GListStore *store,
102 guint number)
103{
104 GObject *object;
105
106 /* 0 cannot be differentiated from NULL, so don't use it */
107 g_assert_cmpint (number, !=, 0);
108
109 object = g_object_new (G_TYPE_OBJECT, NULL);
110 g_object_set_qdata (object, quark: number_quark, GUINT_TO_POINTER (number));
111 g_list_store_append (store, item: object);
112 g_object_unref (object);
113}
114
115static GListStore *
116add_store (GListStore *store,
117 guint start,
118 guint end,
119 guint step)
120{
121 GListStore *child;
122
123 child = new_store (start, end, step);
124 g_list_store_append (store, item: child);
125 g_object_unref (object: child);
126
127 return child;
128}
129
130#define assert_model(model, expected) G_STMT_START{ \
131 char *s = model_to_string (G_LIST_MODEL (model)); \
132 if (!g_str_equal (s, expected)) \
133 g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
134 #model " == " #expected, s, "==", expected); \
135 g_free (s); \
136}G_STMT_END
137
138#define assert_changes(model, expected) G_STMT_START{ \
139 GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \
140 if (!g_str_equal (changes->str, expected)) \
141 g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
142 #model " == " #expected, changes->str, "==", expected); \
143 g_string_set_size (changes, 0); \
144}G_STMT_END
145
146static GListStore *
147new_empty_store (void)
148{
149 return g_list_store_new (G_TYPE_OBJECT);
150}
151
152static GListStore *
153new_store (guint start,
154 guint end,
155 guint step)
156{
157 GListStore *store = new_empty_store ();
158 guint i;
159
160 for (i = start; i <= end; i += step)
161 add (store, number: i);
162
163 return store;
164}
165
166static void
167items_changed (GListModel *model,
168 guint position,
169 guint removed,
170 guint added,
171 GString *changes)
172{
173 g_assert_true (removed != 0 || added != 0);
174
175 if (changes->len)
176 g_string_append (string: changes, val: ", ");
177
178 if (removed == 1 && added == 0)
179 {
180 g_string_append_printf (string: changes, format: "-%u", position);
181 }
182 else if (removed == 0 && added == 1)
183 {
184 g_string_append_printf (string: changes, format: "+%u", position);
185 }
186 else
187 {
188 g_string_append_printf (string: changes, format: "%u", position);
189 if (removed > 0)
190 g_string_append_printf (string: changes, format: "-%u", removed);
191 if (added > 0)
192 g_string_append_printf (string: changes, format: "+%u", added);
193 }
194}
195
196static void
197free_changes (gpointer data)
198{
199 GString *changes = data;
200
201 /* all changes must have been checked via assert_changes() before */
202 g_assert_cmpstr (changes->str, ==, "");
203
204 g_string_free (string: changes, TRUE);
205}
206
207static GtkFlattenListModel *
208new_model (GListStore *store)
209{
210 GtkFlattenListModel *result;
211 GString *changes;
212
213 if (store)
214 g_object_ref (store);
215 result = gtk_flatten_list_model_new (model: G_LIST_MODEL (ptr: store));
216 changes = g_string_new (init: "");
217 g_object_set_qdata_full (G_OBJECT(result), quark: changes_quark, data: changes, destroy: free_changes);
218 g_signal_connect (result, "items-changed", G_CALLBACK (items_changed), changes);
219
220 return result;
221}
222
223static void
224test_create_empty (void)
225{
226 GtkFlattenListModel *flat;
227
228 flat = new_model (NULL);
229 assert_model (flat, "");
230 assert_changes (flat, "");
231
232 g_object_unref (object: flat);
233}
234
235static void
236test_create (void)
237{
238 GtkFlattenListModel *flat;
239 GListStore *model;
240
241 model = g_list_store_new (G_TYPE_LIST_MODEL);
242 add_store (store: model, start: 1, end: 3, step: 1);
243 add_store (store: model, start: 4, end: 4, step: 1);
244 add_store (store: model, start: 5, end: 7, step: 1);
245 add_store (store: model, start: 8, end: 10, step: 1);
246 flat = new_model (store: model);
247 assert_model (flat, "1 2 3 4 5 6 7 8 9 10");
248 assert_changes (flat, "");
249
250 g_object_unref (object: model);
251 assert_model (flat, "1 2 3 4 5 6 7 8 9 10");
252 assert_changes (flat, "");
253
254 g_object_unref (object: flat);
255}
256
257static void
258test_model_add (void)
259{
260 GtkFlattenListModel *flat;
261 GListStore *model;
262
263 model = g_list_store_new (G_TYPE_LIST_MODEL);
264 flat = new_model (store: model);
265 assert_model (flat, "");
266 assert_changes (flat, "");
267
268 add_store (store: model, start: 1, end: 3, step: 1);
269 add_store (store: model, start: 4, end: 4, step: 1);
270 add_store (store: model, start: 5, end: 7, step: 1);
271 add_store (store: model, start: 8, end: 10, step: 1);
272
273 assert_model (flat, "1 2 3 4 5 6 7 8 9 10");
274 assert_changes (flat, "0+3, +3, 4+3, 7+3");
275
276 g_object_unref (object: model);
277 g_object_unref (object: flat);
278}
279
280static void
281test_submodel_add (void)
282{
283 GtkFlattenListModel *flat;
284 GListStore *model, *store[4];
285
286 model = g_list_store_new (G_TYPE_LIST_MODEL);
287 flat = new_model (store: model);
288 assert_model (flat, "");
289 assert_changes (flat, "");
290
291 store[0] = add_store (store: model, start: 2, end: 3, step: 1);
292 store[1] = add_store (store: model, start: 4, end: 4, step: 1);
293 store[2] = add_store (store: model, start: 5, end: 4, step: 1);
294 store[3] = add_store (store: model, start: 8, end: 8, step: 1);
295 assert_model (flat, "2 3 4 8");
296 assert_changes (flat, "0+2, +2, +3");
297
298 insert (store: store[0], pos: 0, number: 1);
299 splice (store: store[2], pos: 0, removed: 0, numbers: (guint[3]) { 5, 6, 7 }, added: 3);
300 splice (store: store[3], pos: 1, removed: 0, numbers: (guint[2]) { 9, 10 }, added: 2);
301 assert_model (flat, "1 2 3 4 5 6 7 8 9 10");
302 assert_changes (flat, "+0, 4+3, 8+2");
303
304 g_object_unref (object: model);
305 g_object_unref (object: flat);
306}
307
308static void
309test_submodel_add2 (void)
310{
311 GtkFlattenListModel *flat;
312 GListStore *model, *store[3];
313
314 model = g_list_store_new (G_TYPE_LIST_MODEL);
315 flat = new_model (store: model);
316 assert_model (flat, "");
317 assert_changes (flat, "");
318
319 store[0] = add_store (store: model, start: 1, end: 0, step: 0);
320 store[1] = add_store (store: model, start: 1, end: 0, step: 0);
321 store[2] = add_store (store: model, start: 1, end: 0, step: 0);
322
323 assert_model (flat, "");
324 assert_changes (flat, "");
325
326 add (store: store[0], number: 1);
327 assert_model (flat, "1");
328 assert_changes (flat, "+0");
329
330 add (store: store[1], number: 3);
331 assert_model (flat, "1 3");
332 assert_changes (flat, "+1");
333
334 add (store: store[0], number: 2);
335 assert_model (flat, "1 2 3");
336 assert_changes (flat, "+1");
337
338 add (store: store[1], number: 4);
339 assert_model (flat, "1 2 3 4");
340 assert_changes (flat, "+3");
341
342 g_object_unref (object: model);
343 g_object_unref (object: flat);
344}
345
346static void
347test_model_remove (void)
348{
349 GtkFlattenListModel *flat;
350 GListStore *model;
351
352 model = g_list_store_new (G_TYPE_LIST_MODEL);
353 add_store (store: model, start: 1, end: 3, step: 1);
354 add_store (store: model, start: 4, end: 4, step: 1);
355 add_store (store: model, start: 5, end: 7, step: 1);
356 add_store (store: model, start: 8, end: 10, step: 1);
357 flat = new_model (store: model);
358 assert_model (flat, "1 2 3 4 5 6 7 8 9 10");
359 assert_changes (flat, "");
360
361 splice (store: model, pos: 1, removed: 2, NULL, added: 0);
362 g_list_store_remove (store: model, position: 1);
363 g_list_store_remove (store: model, position: 0);
364 g_object_unref (object: model);
365 assert_model (flat, "");
366 assert_changes (flat, "3-4, 3-3, 0-3");
367
368 g_object_unref (object: flat);
369}
370
371static void
372test_submodel_remove (void)
373{
374 GtkFlattenListModel *flat;
375 GListStore *model, *store[4];
376
377 model = g_list_store_new (G_TYPE_LIST_MODEL);
378 store[0] = add_store (store: model, start: 1, end: 3, step: 1);
379 store[1] = add_store (store: model, start: 4, end: 4, step: 1);
380 store[2] = add_store (store: model, start: 5, end: 7, step: 1);
381 store[3] = add_store (store: model, start: 8, end: 10, step: 1);
382 flat = new_model (store: model);
383 assert_model (flat, "1 2 3 4 5 6 7 8 9 10");
384 assert_changes (flat, "");
385
386 g_list_store_remove (store: store[0], position: 0);
387 splice (store: store[2], pos: 0, removed: 3, NULL, added: 0);
388 splice (store: store[3], pos: 1, removed: 2, NULL, added: 0);
389 g_object_unref (object: model);
390
391 assert_model (flat, "2 3 4 8");
392 assert_changes (flat, "-0, 3-3, 4-2");
393
394 g_object_unref (object: flat);
395}
396
397int
398main (int argc, char *argv[])
399{
400 (g_test_init) (argc: &argc, argv: &argv, NULL);
401 setlocale (LC_ALL, locale: "C");
402
403 number_quark = g_quark_from_static_string (string: "Hell and fire was spawned to be released.");
404 changes_quark = g_quark_from_static_string (string: "What did I see? Can I believe what I saw?");
405
406 g_test_add_func (testpath: "/flattenlistmodel/create_empty", test_func: test_create_empty);
407 g_test_add_func (testpath: "/flattenlistmodel/create", test_func: test_create);
408 g_test_add_func (testpath: "/flattenlistmodel/model/add", test_func: test_model_add);
409#if GLIB_CHECK_VERSION (2, 58, 0) /* g_list_store_splice() is broken before 2.58 */
410 g_test_add_func (testpath: "/flattenlistmodel/submodel/add", test_func: test_submodel_add);
411 g_test_add_func (testpath: "/flattenlistmodel/submodel/add2", test_func: test_submodel_add2);
412 g_test_add_func (testpath: "/flattenlistmodel/model/remove", test_func: test_model_remove);
413 g_test_add_func (testpath: "/flattenlistmodel/submodel/remove", test_func: test_submodel_remove);
414#endif
415
416 return g_test_run ();
417}
418

source code of gtk/testsuite/gtk/flattenlistmodel.c