1 /* GtkSorter tests.
2  *
3  * Copyright (C) 2019, Red Hat, Inc.
4  * Authors: Benjamin Otte <otte@gnome.org>
5  *          Matthias Clasen <mclasen@redhat.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <locale.h>
22 
23 #include <gtk/gtk.h>
24 
25 static GQuark number_quark;
26 
27 static guint
get_number(GObject * object)28 get_number (GObject *object)
29 {
30   return GPOINTER_TO_UINT (g_object_get_qdata (object, number_quark));
31 }
32 
33 static guint
get_with_parents(GObject * object)34 get_with_parents (GObject *object)
35 {
36   guint number;
37 
38   if (object == NULL)
39     return 0;
40 
41   if (GTK_IS_TREE_LIST_ROW (object))
42     {
43       GtkTreeListRow *r;
44 
45       r = GTK_TREE_LIST_ROW (object);
46       object = gtk_tree_list_row_get_item (r);
47       number = 10 * get_with_parents (G_OBJECT (gtk_tree_list_row_get_parent (r)));
48       g_object_unref (r);
49     }
50   else
51     number = 0;
52 
53   number += get_number (object);
54   g_object_unref (object);
55 
56   return number;
57 }
58 
59 static char *
model_to_string(GListModel * model)60 model_to_string (GListModel *model)
61 {
62   GString *string = g_string_new (NULL);
63   guint i;
64 
65   for (i = 0; i < g_list_model_get_n_items (model); i++)
66     {
67       GObject *object = g_list_model_get_item (model, i);
68 
69       if (i > 0)
70         g_string_append (string, " ");
71       g_string_append_printf (string, "%u", get_with_parents (object));
72       /* no unref since get_with_parents consumes the ref */
73     }
74 
75   return g_string_free (string, FALSE);
76 }
77 
78 static GListStore *
79 new_store (guint start,
80            guint end,
81            guint step);
82 
83 static void
add(GListStore * store,guint number)84 add (GListStore *store,
85      guint       number)
86 {
87   GObject *object;
88 
89   /* 0 cannot be differentiated from NULL, so don't use it */
90   g_assert_cmpint (number, !=, 0);
91 
92   object = g_object_new (G_TYPE_OBJECT, NULL);
93   g_object_set_qdata (object, number_quark, GUINT_TO_POINTER (number));
94   g_list_store_append (store, object);
95   g_object_unref (object);
96 }
97 
98 #define assert_model(model, expected) G_STMT_START{ \
99   char *s = model_to_string (G_LIST_MODEL (model)); \
100   if (!g_str_equal (s, expected)) \
101      g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
102          #model " == " #expected, s, "==", expected); \
103   g_free (s); \
104 }G_STMT_END
105 
106 #define assert_not_model(model, expected) G_STMT_START{ \
107   char *s = model_to_string (G_LIST_MODEL (model)); \
108   if (g_str_equal (s, expected)) \
109      g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
110          #model " != " #expected, s, "!=", expected); \
111   g_free (s); \
112 }G_STMT_END
113 
114 /* This could be faster by foreach()ing through the models and comparing
115  * the item pointers */
116 #define assert_model_equal(model1, model2) G_STMT_START{\
117   char *s1 = model_to_string (G_LIST_MODEL (model1)); \
118   char *s2 = model_to_string (G_LIST_MODEL (model2)); \
119   if (!g_str_equal (s1, s2)) \
120      g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
121          #model1 " != " #model2, s1, "==", s2); \
122   g_free (s2); \
123   g_free (s1); \
124 }G_STMT_END
125 
126 static GListStore *
new_empty_store(void)127 new_empty_store (void)
128 {
129   return g_list_store_new (G_TYPE_OBJECT);
130 }
131 
132 static GListStore *
new_store(guint start,guint end,guint step)133 new_store (guint start,
134            guint end,
135            guint step)
136 {
137   GListStore *store = new_empty_store ();
138   guint i;
139 
140   for (i = start; i <= end; i += step)
141     add (store, i);
142 
143   return store;
144 }
145 
146 static GListModel *
new_child_model(gpointer item,gpointer unused)147 new_child_model (gpointer item,
148                  gpointer unused)
149 {
150   guint n = get_number (item);
151 
152   if (n == 1)
153     return NULL;
154 
155   return G_LIST_MODEL (new_store (1, n - 1, 1));
156 }
157 
158 static GListModel *
new_model(guint size)159 new_model (guint size)
160 {
161   return G_LIST_MODEL (gtk_tree_list_model_new (G_LIST_MODEL (new_store (1, size, 1)),
162                                                 FALSE,
163                                                 TRUE,
164                                                 new_child_model,
165                                                 NULL, NULL));
166 }
167 
168 static void
test_simple(void)169 test_simple (void)
170 {
171   GListModel *model;
172   GtkSortListModel *sort;
173   GtkSorter *sorter;
174 
175   model = new_model (3);
176   assert_model (model, "1 2 21 3 31 32 321");
177 
178   sorter = GTK_SORTER (gtk_tree_list_row_sorter_new (NULL));
179   sort = gtk_sort_list_model_new (model, sorter);
180   assert_model (sort, "1 2 21 3 31 32 321");
181 
182   g_object_unref (sort);
183 }
184 
185 static GtkSorter *
new_numeric_sorter(void)186 new_numeric_sorter (void)
187 {
188   return GTK_SORTER (gtk_numeric_sorter_new (gtk_cclosure_expression_new (G_TYPE_UINT, NULL, 0, NULL, (GCallback)get_number, NULL, NULL)));
189 }
190 
191 static void
test_compare_total_order(void)192 test_compare_total_order (void)
193 {
194   GListModel *model;
195   GtkSorter *sorter;
196   guint i, j, n;
197 
198   model = new_model (3);
199   assert_model (model, "1 2 21 3 31 32 321");
200 
201   sorter = GTK_SORTER (gtk_tree_list_row_sorter_new (new_numeric_sorter ()));
202 
203   n = g_list_model_get_n_items (model);
204   for (i = 0; i < n; i++)
205     {
206       gpointer item1 = g_list_model_get_item (model, i);
207       for (j = 0; j < n; j++)
208         {
209           gpointer item2 = g_list_model_get_item (model, j);
210 
211           g_assert_cmpint (gtk_sorter_compare (sorter, item1, item2), ==, gtk_ordering_from_cmpfunc ((int) i - j));
212           g_object_unref (item2);
213         }
214       g_object_unref (item1);
215     }
216 
217   g_object_unref (sorter);
218   g_object_unref (model);
219 }
220 
221 static void
test_compare_no_order(void)222 test_compare_no_order (void)
223 {
224   GListModel *model;
225   GtkSorter *sorter;
226   guint i, j, n;
227 
228   model = new_model (3);
229   assert_model (model, "1 2 21 3 31 32 321");
230 
231   sorter = GTK_SORTER (gtk_tree_list_row_sorter_new (NULL));
232 
233   n = g_list_model_get_n_items (model);
234   for (i = 0; i < n; i++)
235     {
236       gpointer item1 = g_list_model_get_item (model, i);
237       for (j = 0; j < n; j++)
238         {
239           gpointer item2 = g_list_model_get_item (model, j);
240 
241           g_assert_cmpint (gtk_sorter_compare (sorter, item1, item2), ==, gtk_ordering_from_cmpfunc ((int) i - j));
242           g_object_unref (item2);
243         }
244       g_object_unref (item1);
245     }
246 
247   g_object_unref (sorter);
248   g_object_unref (model);
249 }
250 
251 int
main(int argc,char * argv[])252 main (int argc, char *argv[])
253 {
254   (g_test_init) (&argc, &argv, NULL);
255   setlocale (LC_ALL, "C");
256 
257   number_quark = g_quark_from_static_string ("Like a trashcan fire in a prison cell");
258 
259   g_test_add_func ("/sorter/simple", test_simple);
260   g_test_add_func ("/sorter/compare-total-order", test_compare_total_order);
261   g_test_add_func ("/sorter/compare-no-order", test_compare_no_order);
262 
263   return g_test_run ();
264 }
265