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 | add (GListStore *store, |
62 | guint number) |
63 | { |
64 | GObject *object; |
65 | |
66 | /* 0 cannot be differentiated from NULL, so don't use it */ |
67 | g_assert_cmpint (number, !=, 0); |
68 | |
69 | object = g_object_new (G_TYPE_OBJECT, NULL); |
70 | g_object_set_qdata (object, quark: number_quark, GUINT_TO_POINTER (number)); |
71 | g_list_store_append (store, item: object); |
72 | g_object_unref (object); |
73 | } |
74 | |
75 | #define assert_model(model, expected) G_STMT_START{ \ |
76 | char *s = model_to_string (G_LIST_MODEL (model)); \ |
77 | if (!g_str_equal (s, expected)) \ |
78 | g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ |
79 | #model " == " #expected, s, "==", expected); \ |
80 | g_free (s); \ |
81 | }G_STMT_END |
82 | |
83 | #define assert_changes(model, expected) G_STMT_START{ \ |
84 | GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \ |
85 | if (!g_str_equal (changes->str, expected)) \ |
86 | g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ |
87 | #model " == " #expected, changes->str, "==", expected); \ |
88 | g_string_set_size (changes, 0); \ |
89 | }G_STMT_END |
90 | |
91 | #define ignore_changes(model) G_STMT_START{ \ |
92 | GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \ |
93 | g_string_set_size (changes, 0); \ |
94 | }G_STMT_END |
95 | |
96 | static GListStore * |
97 | new_empty_store (void) |
98 | { |
99 | return g_list_store_new (G_TYPE_OBJECT); |
100 | } |
101 | |
102 | static GListStore * |
103 | new_store (guint start, |
104 | guint end, |
105 | guint step) |
106 | { |
107 | GListStore *store = new_empty_store (); |
108 | guint i; |
109 | |
110 | for (i = start; i <= end; i += step) |
111 | add (store, number: i); |
112 | |
113 | return store; |
114 | } |
115 | |
116 | static void |
117 | items_changed (GListModel *model, |
118 | guint position, |
119 | guint removed, |
120 | guint added, |
121 | GString *changes) |
122 | { |
123 | g_assert_true (removed != 0 || added != 0); |
124 | |
125 | if (changes->len) |
126 | g_string_append (string: changes, val: ", " ); |
127 | |
128 | if (removed == 1 && added == 0) |
129 | { |
130 | g_string_append_printf (string: changes, format: "-%u" , position); |
131 | } |
132 | else if (removed == 0 && added == 1) |
133 | { |
134 | g_string_append_printf (string: changes, format: "+%u" , position); |
135 | } |
136 | else |
137 | { |
138 | g_string_append_printf (string: changes, format: "%u" , position); |
139 | if (removed > 0) |
140 | g_string_append_printf (string: changes, format: "-%u" , removed); |
141 | if (added > 0) |
142 | g_string_append_printf (string: changes, format: "+%u" , added); |
143 | } |
144 | } |
145 | |
146 | static void |
147 | free_changes (gpointer data) |
148 | { |
149 | GString *changes = data; |
150 | |
151 | /* all changes must have been checked via assert_changes() before */ |
152 | g_assert_cmpstr (changes->str, ==, "" ); |
153 | |
154 | g_string_free (string: changes, TRUE); |
155 | } |
156 | |
157 | static GtkFilterListModel * |
158 | new_model (guint size, |
159 | GtkCustomFilterFunc filter_func, |
160 | gpointer data) |
161 | { |
162 | GtkFilterListModel *result; |
163 | GtkFilter *filter; |
164 | GString *changes; |
165 | |
166 | if (filter_func) |
167 | filter = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: filter_func, user_data: data, NULL)); |
168 | else |
169 | filter = NULL; |
170 | result = gtk_filter_list_model_new (g_object_ref (G_LIST_MODEL (new_store (1, size, 1))), filter); |
171 | changes = g_string_new (init: "" ); |
172 | g_object_set_qdata_full (G_OBJECT(result), quark: changes_quark, data: changes, destroy: free_changes); |
173 | g_signal_connect (result, "items-changed" , G_CALLBACK (items_changed), changes); |
174 | |
175 | return result; |
176 | } |
177 | |
178 | static gboolean |
179 | is_smaller_than (gpointer item, |
180 | gpointer data) |
181 | { |
182 | return g_object_get_qdata (object: item, quark: number_quark) < data; |
183 | } |
184 | |
185 | static gboolean |
186 | is_larger_than (gpointer item, |
187 | gpointer data) |
188 | { |
189 | return g_object_get_qdata (object: item, quark: number_quark) > data; |
190 | } |
191 | |
192 | static gboolean |
193 | is_near (gpointer item, |
194 | gpointer data) |
195 | { |
196 | return ABS (GPOINTER_TO_INT (g_object_get_qdata (item, number_quark)) - GPOINTER_TO_INT (data)) <= 2; |
197 | } |
198 | |
199 | static gboolean |
200 | is_not_near (gpointer item, |
201 | gpointer data) |
202 | { |
203 | return ABS (GPOINTER_TO_INT (g_object_get_qdata (item, number_quark)) - GPOINTER_TO_INT (data)) > 2; |
204 | } |
205 | |
206 | static void |
207 | test_create (void) |
208 | { |
209 | GtkFilterListModel *filter; |
210 | |
211 | filter = new_model (size: 10, NULL, NULL); |
212 | assert_model (filter, "1 2 3 4 5 6 7 8 9 10" ); |
213 | assert_changes (filter, "" ); |
214 | g_object_unref (object: filter); |
215 | |
216 | filter = new_model (size: 10, filter_func: is_smaller_than, GUINT_TO_POINTER (20)); |
217 | assert_model (filter, "1 2 3 4 5 6 7 8 9 10" ); |
218 | assert_changes (filter, "" ); |
219 | g_object_unref (object: filter); |
220 | |
221 | filter = new_model (size: 10, filter_func: is_smaller_than, GUINT_TO_POINTER (7)); |
222 | assert_model (filter, "1 2 3 4 5 6" ); |
223 | assert_changes (filter, "" ); |
224 | g_object_unref (object: filter); |
225 | |
226 | filter = new_model (size: 10, filter_func: is_smaller_than, GUINT_TO_POINTER (0)); |
227 | assert_model (filter, "" ); |
228 | assert_changes (filter, "" ); |
229 | g_object_unref (object: filter); |
230 | } |
231 | |
232 | static void |
233 | test_empty_set_filter (void) |
234 | { |
235 | GtkFilterListModel *filter; |
236 | GtkFilter *custom; |
237 | |
238 | filter = new_model (size: 10, NULL, NULL); |
239 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_smaller_than, GUINT_TO_POINTER (20), NULL)); |
240 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
241 | g_object_unref (object: custom); |
242 | assert_model (filter, "1 2 3 4 5 6 7 8 9 10" ); |
243 | assert_changes (filter, "" ); |
244 | g_object_unref (object: filter); |
245 | |
246 | filter = new_model (size: 10, NULL, NULL); |
247 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_smaller_than, GUINT_TO_POINTER (7), NULL)); |
248 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
249 | g_object_unref (object: custom); |
250 | assert_model (filter, "1 2 3 4 5 6" ); |
251 | assert_changes (filter, "6-4" ); |
252 | g_object_unref (object: filter); |
253 | |
254 | filter = new_model (size: 10, NULL, NULL); |
255 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_smaller_than, GUINT_TO_POINTER (0), NULL)); |
256 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
257 | g_object_unref (object: custom); |
258 | assert_model (filter, "" ); |
259 | assert_changes (filter, "0-10" ); |
260 | g_object_unref (object: filter); |
261 | |
262 | filter = new_model (size: 10, NULL, NULL); |
263 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_larger_than, GUINT_TO_POINTER (0), NULL)); |
264 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
265 | g_object_unref (object: custom); |
266 | assert_model (filter, "1 2 3 4 5 6 7 8 9 10" ); |
267 | assert_changes (filter, "" ); |
268 | g_object_unref (object: filter); |
269 | |
270 | filter = new_model (size: 10, NULL, NULL); |
271 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_larger_than, GUINT_TO_POINTER (3), NULL)); |
272 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
273 | g_object_unref (object: custom); |
274 | assert_model (filter, "4 5 6 7 8 9 10" ); |
275 | assert_changes (filter, "0-3" ); |
276 | g_object_unref (object: filter); |
277 | |
278 | filter = new_model (size: 10, NULL, NULL); |
279 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_larger_than, GUINT_TO_POINTER (20), NULL)); |
280 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
281 | g_object_unref (object: custom); |
282 | assert_model (filter, "" ); |
283 | assert_changes (filter, "0-10" ); |
284 | g_object_unref (object: filter); |
285 | |
286 | filter = new_model (size: 10, NULL, NULL); |
287 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_near, GUINT_TO_POINTER (5), NULL)); |
288 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
289 | g_object_unref (object: custom); |
290 | assert_model (filter, "3 4 5 6 7" ); |
291 | assert_changes (filter, "0-10+5" ); |
292 | g_object_unref (object: filter); |
293 | |
294 | filter = new_model (size: 10, NULL, NULL); |
295 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_not_near, GUINT_TO_POINTER (5), NULL)); |
296 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
297 | g_object_unref (object: custom); |
298 | assert_model (filter, "1 2 8 9 10" ); |
299 | assert_changes (filter, "2-5" ); |
300 | g_object_unref (object: filter); |
301 | } |
302 | |
303 | static void |
304 | test_change_filter (void) |
305 | { |
306 | GtkFilterListModel *filter; |
307 | GtkFilter *custom; |
308 | |
309 | filter = new_model (size: 10, filter_func: is_not_near, GUINT_TO_POINTER (5)); |
310 | assert_model (filter, "1 2 8 9 10" ); |
311 | assert_changes (filter, "" ); |
312 | |
313 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_not_near, GUINT_TO_POINTER (6), NULL)); |
314 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
315 | g_object_unref (object: custom); |
316 | assert_model (filter, "1 2 3 9 10" ); |
317 | assert_changes (filter, "2-1+1" ); |
318 | |
319 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_not_near, GUINT_TO_POINTER (9), NULL)); |
320 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
321 | g_object_unref (object: custom); |
322 | assert_model (filter, "1 2 3 4 5 6" ); |
323 | assert_changes (filter, "3-2+3" ); |
324 | |
325 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_smaller_than, GUINT_TO_POINTER (6), NULL)); |
326 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
327 | g_object_unref (object: custom); |
328 | assert_model (filter, "1 2 3 4 5" ); |
329 | assert_changes (filter, "-5" ); |
330 | |
331 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_larger_than, GUINT_TO_POINTER (4), NULL)); |
332 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
333 | g_object_unref (object: custom); |
334 | assert_model (filter, "5 6 7 8 9 10" ); |
335 | assert_changes (filter, "0-5+6" ); |
336 | |
337 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_not_near, GUINT_TO_POINTER (2), NULL)); |
338 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
339 | g_object_unref (object: custom); |
340 | assert_model (filter, "5 6 7 8 9 10" ); |
341 | assert_changes (filter, "" ); |
342 | |
343 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_not_near, GUINT_TO_POINTER (4), NULL)); |
344 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
345 | g_object_unref (object: custom); |
346 | assert_model (filter, "1 7 8 9 10" ); |
347 | assert_changes (filter, "0-2+1" ); |
348 | |
349 | g_object_unref (object: filter); |
350 | } |
351 | |
352 | static void |
353 | test_incremental (void) |
354 | { |
355 | GtkFilterListModel *filter; |
356 | GtkFilter *custom; |
357 | |
358 | /* everything is filtered */ |
359 | filter = new_model (size: 1000, filter_func: is_larger_than, GUINT_TO_POINTER (10000)); |
360 | gtk_filter_list_model_set_incremental (self: filter, TRUE); |
361 | assert_model (filter, "" ); |
362 | assert_changes (filter, "" ); |
363 | |
364 | custom = GTK_FILTER (ptr: gtk_custom_filter_new (match_func: is_near, GUINT_TO_POINTER (512), NULL)); |
365 | gtk_filter_list_model_set_filter (self: filter, filter: custom); |
366 | g_object_unref (object: custom); |
367 | assert_model (filter, "" ); |
368 | assert_changes (filter, "" ); |
369 | |
370 | while (g_main_context_pending (NULL)) |
371 | g_main_context_iteration (NULL, TRUE); |
372 | assert_model (filter, "510 511 512 513 514" ); |
373 | /* implementation detail */ |
374 | ignore_changes (filter); |
375 | |
376 | g_object_unref (object: filter); |
377 | } |
378 | |
379 | static void |
380 | test_empty (void) |
381 | { |
382 | GtkFilterListModel *filter; |
383 | GListStore *store; |
384 | GtkFilter *f; |
385 | |
386 | filter = gtk_filter_list_model_new (NULL, NULL); |
387 | |
388 | g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (filter)), ==, 0); |
389 | g_assert_null (g_list_model_get_item (G_LIST_MODEL (filter), 11)); |
390 | |
391 | store = g_list_store_new (G_TYPE_OBJECT); |
392 | gtk_filter_list_model_set_model (self: filter, model: G_LIST_MODEL (ptr: store)); |
393 | g_object_unref (object: store); |
394 | |
395 | g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (filter)), ==, 0); |
396 | g_assert_null (g_list_model_get_item (G_LIST_MODEL (filter), 11)); |
397 | |
398 | f = GTK_FILTER (ptr: gtk_every_filter_new ()); |
399 | gtk_filter_list_model_set_filter (self: filter, filter: f); |
400 | g_object_unref (object: f); |
401 | |
402 | g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (filter)), ==, 0); |
403 | g_assert_null (g_list_model_get_item (G_LIST_MODEL (filter), 11)); |
404 | |
405 | g_object_unref (object: filter); |
406 | } |
407 | |
408 | int |
409 | main (int argc, char *argv[]) |
410 | { |
411 | (g_test_init) (argc: &argc, argv: &argv, NULL); |
412 | setlocale (LC_ALL, locale: "C" ); |
413 | |
414 | number_quark = g_quark_from_static_string (string: "Hell and fire was spawned to be released." ); |
415 | changes_quark = g_quark_from_static_string (string: "What did I see? Can I believe what I saw?" ); |
416 | |
417 | g_test_add_func (testpath: "/filterlistmodel/create" , test_func: test_create); |
418 | g_test_add_func (testpath: "/filterlistmodel/empty_set_filter" , test_func: test_empty_set_filter); |
419 | g_test_add_func (testpath: "/filterlistmodel/change_filter" , test_func: test_change_filter); |
420 | g_test_add_func (testpath: "/filterlistmodel/incremental" , test_func: test_incremental); |
421 | g_test_add_func (testpath: "/filterlistmodel/empty" , test_func: test_empty); |
422 | |
423 | return g_test_run (); |
424 | } |
425 | |