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
get(GListModel * model,guint position)28 get (GListModel *model,
29      guint       position)
30 {
31   GObject *object = g_list_model_get_item (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 *
model_to_string(GListModel * model)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 (model); i++)
46     {
47       if (i > 0)
48         g_string_append (string, " ");
49       g_string_append_printf (string, "%u", get (model, 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
add(GListStore * store,guint number)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, number_quark, GUINT_TO_POINTER (number));
71   g_list_store_append (store, 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 *
new_empty_store(void)97 new_empty_store (void)
98 {
99   return g_list_store_new (G_TYPE_OBJECT);
100 }
101 
102 static GListStore *
new_store(guint start,guint end,guint step)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, i);
112 
113   return store;
114 }
115 
116 static void
items_changed(GListModel * model,guint position,guint removed,guint added,GString * changes)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 (changes, ", ");
127 
128   if (removed == 1 && added == 0)
129     {
130       g_string_append_printf (changes, "-%u", position);
131     }
132   else if (removed == 0 && added == 1)
133     {
134       g_string_append_printf (changes, "+%u", position);
135     }
136   else
137     {
138       g_string_append_printf (changes, "%u", position);
139       if (removed > 0)
140         g_string_append_printf (changes, "-%u", removed);
141       if (added > 0)
142         g_string_append_printf (changes, "+%u", added);
143     }
144 }
145 
146 static void
free_changes(gpointer data)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 (changes, TRUE);
155 }
156 
157 static GtkFilterListModel *
new_model(guint size,GtkCustomFilterFunc filter_func,gpointer data)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 (gtk_custom_filter_new (filter_func, 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 ("");
172   g_object_set_qdata_full (G_OBJECT(result), changes_quark, changes, free_changes);
173   g_signal_connect (result, "items-changed", G_CALLBACK (items_changed), changes);
174 
175   return result;
176 }
177 
178 static gboolean
is_smaller_than(gpointer item,gpointer data)179 is_smaller_than (gpointer item,
180                  gpointer data)
181 {
182   return g_object_get_qdata (item, number_quark) < data;
183 }
184 
185 static gboolean
is_larger_than(gpointer item,gpointer data)186 is_larger_than (gpointer item,
187                 gpointer data)
188 {
189   return g_object_get_qdata (item, number_quark) > data;
190 }
191 
192 static gboolean
is_near(gpointer item,gpointer data)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
is_not_near(gpointer item,gpointer data)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
test_create(void)207 test_create (void)
208 {
209   GtkFilterListModel *filter;
210 
211   filter = new_model (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 (filter);
215 
216   filter = new_model (10, 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 (filter);
220 
221   filter = new_model (10, 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 (filter);
225 
226   filter = new_model (10, is_smaller_than, GUINT_TO_POINTER (0));
227   assert_model (filter, "");
228   assert_changes (filter, "");
229   g_object_unref (filter);
230 }
231 
232 static void
test_empty_set_filter(void)233 test_empty_set_filter (void)
234 {
235   GtkFilterListModel *filter;
236   GtkFilter *custom;
237 
238   filter = new_model (10, NULL, NULL);
239   custom = GTK_FILTER (gtk_custom_filter_new (is_smaller_than, GUINT_TO_POINTER (20), NULL));
240   gtk_filter_list_model_set_filter (filter, custom);
241   g_object_unref (custom);
242   assert_model (filter, "1 2 3 4 5 6 7 8 9 10");
243   assert_changes (filter, "");
244   g_object_unref (filter);
245 
246   filter = new_model (10, NULL, NULL);
247   custom = GTK_FILTER (gtk_custom_filter_new (is_smaller_than, GUINT_TO_POINTER (7), NULL));
248   gtk_filter_list_model_set_filter (filter, custom);
249   g_object_unref (custom);
250   assert_model (filter, "1 2 3 4 5 6");
251   assert_changes (filter, "6-4");
252   g_object_unref (filter);
253 
254   filter = new_model (10, NULL, NULL);
255   custom = GTK_FILTER (gtk_custom_filter_new (is_smaller_than, GUINT_TO_POINTER (0), NULL));
256   gtk_filter_list_model_set_filter (filter, custom);
257   g_object_unref (custom);
258   assert_model (filter, "");
259   assert_changes (filter, "0-10");
260   g_object_unref (filter);
261 
262   filter = new_model (10, NULL, NULL);
263   custom = GTK_FILTER (gtk_custom_filter_new (is_larger_than, GUINT_TO_POINTER (0), NULL));
264   gtk_filter_list_model_set_filter (filter, custom);
265   g_object_unref (custom);
266   assert_model (filter, "1 2 3 4 5 6 7 8 9 10");
267   assert_changes (filter, "");
268   g_object_unref (filter);
269 
270   filter = new_model (10, NULL, NULL);
271   custom = GTK_FILTER (gtk_custom_filter_new (is_larger_than, GUINT_TO_POINTER (3), NULL));
272   gtk_filter_list_model_set_filter (filter, custom);
273   g_object_unref (custom);
274   assert_model (filter, "4 5 6 7 8 9 10");
275   assert_changes (filter, "0-3");
276   g_object_unref (filter);
277 
278   filter = new_model (10, NULL, NULL);
279   custom = GTK_FILTER (gtk_custom_filter_new (is_larger_than, GUINT_TO_POINTER (20), NULL));
280   gtk_filter_list_model_set_filter (filter, custom);
281   g_object_unref (custom);
282   assert_model (filter, "");
283   assert_changes (filter, "0-10");
284   g_object_unref (filter);
285 
286   filter = new_model (10, NULL, NULL);
287   custom = GTK_FILTER (gtk_custom_filter_new (is_near, GUINT_TO_POINTER (5), NULL));
288   gtk_filter_list_model_set_filter (filter, custom);
289   g_object_unref (custom);
290   assert_model (filter, "3 4 5 6 7");
291   assert_changes (filter, "0-10+5");
292   g_object_unref (filter);
293 
294   filter = new_model (10, NULL, NULL);
295   custom = GTK_FILTER (gtk_custom_filter_new (is_not_near, GUINT_TO_POINTER (5), NULL));
296   gtk_filter_list_model_set_filter (filter, custom);
297   g_object_unref (custom);
298   assert_model (filter, "1 2 8 9 10");
299   assert_changes (filter, "2-5");
300   g_object_unref (filter);
301 }
302 
303 static void
test_change_filter(void)304 test_change_filter (void)
305 {
306   GtkFilterListModel *filter;
307   GtkFilter *custom;
308 
309   filter = new_model (10, 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 (gtk_custom_filter_new (is_not_near, GUINT_TO_POINTER (6), NULL));
314   gtk_filter_list_model_set_filter (filter, custom);
315   g_object_unref (custom);
316   assert_model (filter, "1 2 3 9 10");
317   assert_changes (filter, "2-1+1");
318 
319   custom = GTK_FILTER (gtk_custom_filter_new (is_not_near, GUINT_TO_POINTER (9), NULL));
320   gtk_filter_list_model_set_filter (filter, custom);
321   g_object_unref (custom);
322   assert_model (filter, "1 2 3 4 5 6");
323   assert_changes (filter, "3-2+3");
324 
325   custom = GTK_FILTER (gtk_custom_filter_new (is_smaller_than, GUINT_TO_POINTER (6), NULL));
326   gtk_filter_list_model_set_filter (filter, custom);
327   g_object_unref (custom);
328   assert_model (filter, "1 2 3 4 5");
329   assert_changes (filter, "-5");
330 
331   custom = GTK_FILTER (gtk_custom_filter_new (is_larger_than, GUINT_TO_POINTER (4), NULL));
332   gtk_filter_list_model_set_filter (filter, custom);
333   g_object_unref (custom);
334   assert_model (filter, "5 6 7 8 9 10");
335   assert_changes (filter, "0-5+6");
336 
337   custom = GTK_FILTER (gtk_custom_filter_new (is_not_near, GUINT_TO_POINTER (2), NULL));
338   gtk_filter_list_model_set_filter (filter, custom);
339   g_object_unref (custom);
340   assert_model (filter, "5 6 7 8 9 10");
341   assert_changes (filter, "");
342 
343   custom = GTK_FILTER (gtk_custom_filter_new (is_not_near, GUINT_TO_POINTER (4), NULL));
344   gtk_filter_list_model_set_filter (filter, custom);
345   g_object_unref (custom);
346   assert_model (filter, "1 7 8 9 10");
347   assert_changes (filter, "0-2+1");
348 
349   g_object_unref (filter);
350 }
351 
352 static void
test_incremental(void)353 test_incremental (void)
354 {
355   GtkFilterListModel *filter;
356   GtkFilter *custom;
357 
358   /* everything is filtered */
359   filter = new_model (1000, is_larger_than, GUINT_TO_POINTER (10000));
360   gtk_filter_list_model_set_incremental (filter, TRUE);
361   assert_model (filter, "");
362   assert_changes (filter, "");
363 
364   custom = GTK_FILTER (gtk_custom_filter_new (is_near, GUINT_TO_POINTER (512), NULL));
365   gtk_filter_list_model_set_filter (filter, custom);
366   g_object_unref (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 (filter);
377 }
378 
379 static void
test_empty(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 (filter, G_LIST_MODEL (store));
393   g_object_unref (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 (gtk_every_filter_new ());
399   gtk_filter_list_model_set_filter (filter, f);
400   g_object_unref (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 (filter);
406 }
407 
408 int
main(int argc,char * argv[])409 main (int argc, char *argv[])
410 {
411   (g_test_init) (&argc, &argv, NULL);
412   setlocale (LC_ALL, "C");
413 
414   number_quark = g_quark_from_static_string ("Hell and fire was spawned to be released.");
415   changes_quark = g_quark_from_static_string ("What did I see? Can I believe what I saw?");
416 
417   g_test_add_func ("/filterlistmodel/create", test_create);
418   g_test_add_func ("/filterlistmodel/empty_set_filter", test_empty_set_filter);
419   g_test_add_func ("/filterlistmodel/change_filter", test_change_filter);
420   g_test_add_func ("/filterlistmodel/incremental", test_incremental);
421   g_test_add_func ("/filterlistmodel/empty", test_empty);
422 
423   return g_test_run ();
424 }
425