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