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 | |
24 | static GQuark number_quark; |
25 | static GQuark changes_quark; |
26 | |
27 | static guint |
28 | get (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 | |
39 | static char * |
40 | model_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 | |
55 | static GListStore * |
56 | new_store (guint start, |
57 | guint end, |
58 | guint step); |
59 | |
60 | static void |
61 | splice (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 | |
84 | static void |
85 | insert (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 | |
100 | static void |
101 | add (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 | |
115 | static GListStore * |
116 | add_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 | |
146 | static GListStore * |
147 | new_empty_store (void) |
148 | { |
149 | return g_list_store_new (G_TYPE_OBJECT); |
150 | } |
151 | |
152 | static GListStore * |
153 | new_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 | |
166 | static void |
167 | items_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 | |
196 | static void |
197 | free_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 | |
207 | static GtkFlattenListModel * |
208 | new_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 | |
223 | static void |
224 | test_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 | |
235 | static void |
236 | test_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 | |
257 | static void |
258 | test_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 | |
280 | static void |
281 | test_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 | |
308 | static void |
309 | test_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 | |
346 | static void |
347 | test_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 | |
371 | static void |
372 | test_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 | |
397 | int |
398 | main (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 | |