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 |
28 | get_number (GObject *object) |
29 | { |
30 | return GPOINTER_TO_UINT (g_object_get_qdata (object, number_quark)); |
31 | } |
32 | |
33 | static guint |
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 (ptr: object)) |
42 | { |
43 | GtkTreeListRow *r; |
44 | |
45 | r = GTK_TREE_LIST_ROW (ptr: object); |
46 | object = gtk_tree_list_row_get_item (self: r); |
47 | number = 10 * get_with_parents (G_OBJECT (gtk_tree_list_row_get_parent (r))); |
48 | g_object_unref (object: 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 * |
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 (list: model); i++) |
66 | { |
67 | GObject *object = g_list_model_get_item (list: model, position: i); |
68 | |
69 | if (i > 0) |
70 | g_string_append (string, val: " " ); |
71 | g_string_append_printf (string, format: "%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 |
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, quark: number_quark, GUINT_TO_POINTER (number)); |
94 | g_list_store_append (store, item: 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 * |
127 | new_empty_store (void) |
128 | { |
129 | return g_list_store_new (G_TYPE_OBJECT); |
130 | } |
131 | |
132 | static GListStore * |
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, number: i); |
142 | |
143 | return store; |
144 | } |
145 | |
146 | static GListModel * |
147 | new_child_model (gpointer item, |
148 | gpointer unused) |
149 | { |
150 | guint n = get_number (object: item); |
151 | |
152 | if (n == 1) |
153 | return NULL; |
154 | |
155 | return G_LIST_MODEL (ptr: new_store (start: 1, end: n - 1, step: 1)); |
156 | } |
157 | |
158 | static GListModel * |
159 | new_model (guint size) |
160 | { |
161 | return G_LIST_MODEL (ptr: gtk_tree_list_model_new (root: G_LIST_MODEL (ptr: new_store (start: 1, end: size, step: 1)), |
162 | FALSE, |
163 | TRUE, |
164 | create_func: new_child_model, |
165 | NULL, NULL)); |
166 | } |
167 | |
168 | static void |
169 | test_simple (void) |
170 | { |
171 | GListModel *model; |
172 | GtkSortListModel *sort; |
173 | GtkSorter *sorter; |
174 | |
175 | model = new_model (size: 3); |
176 | assert_model (model, "1 2 21 3 31 32 321" ); |
177 | |
178 | sorter = GTK_SORTER (ptr: 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 (object: sort); |
183 | } |
184 | |
185 | static GtkSorter * |
186 | new_numeric_sorter (void) |
187 | { |
188 | return GTK_SORTER (ptr: gtk_numeric_sorter_new (expression: gtk_cclosure_expression_new (G_TYPE_UINT, NULL, n_params: 0, NULL, callback_func: (GCallback)get_number, NULL, NULL))); |
189 | } |
190 | |
191 | static void |
192 | test_compare_total_order (void) |
193 | { |
194 | GListModel *model; |
195 | GtkSorter *sorter; |
196 | guint i, j, n; |
197 | |
198 | model = new_model (size: 3); |
199 | assert_model (model, "1 2 21 3 31 32 321" ); |
200 | |
201 | sorter = GTK_SORTER (ptr: gtk_tree_list_row_sorter_new (sorter: new_numeric_sorter ())); |
202 | |
203 | n = g_list_model_get_n_items (list: model); |
204 | for (i = 0; i < n; i++) |
205 | { |
206 | gpointer item1 = g_list_model_get_item (list: model, position: i); |
207 | for (j = 0; j < n; j++) |
208 | { |
209 | gpointer item2 = g_list_model_get_item (list: model, position: j); |
210 | |
211 | g_assert_cmpint (gtk_sorter_compare (sorter, item1, item2), ==, gtk_ordering_from_cmpfunc ((int) i - j)); |
212 | g_object_unref (object: item2); |
213 | } |
214 | g_object_unref (object: item1); |
215 | } |
216 | |
217 | g_object_unref (object: sorter); |
218 | g_object_unref (object: model); |
219 | } |
220 | |
221 | static void |
222 | test_compare_no_order (void) |
223 | { |
224 | GListModel *model; |
225 | GtkSorter *sorter; |
226 | guint i, j, n; |
227 | |
228 | model = new_model (size: 3); |
229 | assert_model (model, "1 2 21 3 31 32 321" ); |
230 | |
231 | sorter = GTK_SORTER (ptr: gtk_tree_list_row_sorter_new (NULL)); |
232 | |
233 | n = g_list_model_get_n_items (list: model); |
234 | for (i = 0; i < n; i++) |
235 | { |
236 | gpointer item1 = g_list_model_get_item (list: model, position: i); |
237 | for (j = 0; j < n; j++) |
238 | { |
239 | gpointer item2 = g_list_model_get_item (list: model, position: j); |
240 | |
241 | g_assert_cmpint (gtk_sorter_compare (sorter, item1, item2), ==, gtk_ordering_from_cmpfunc ((int) i - j)); |
242 | g_object_unref (object: item2); |
243 | } |
244 | g_object_unref (object: item1); |
245 | } |
246 | |
247 | g_object_unref (object: sorter); |
248 | g_object_unref (object: model); |
249 | } |
250 | |
251 | int |
252 | main (int argc, char *argv[]) |
253 | { |
254 | (g_test_init) (argc: &argc, argv: &argv, NULL); |
255 | setlocale (LC_ALL, locale: "C" ); |
256 | |
257 | number_quark = g_quark_from_static_string (string: "Like a trashcan fire in a prison cell" ); |
258 | |
259 | g_test_add_func (testpath: "/sorter/simple" , test_func: test_simple); |
260 | g_test_add_func (testpath: "/sorter/compare-total-order" , test_func: test_compare_total_order); |
261 | g_test_add_func (testpath: "/sorter/compare-no-order" , test_func: test_compare_no_order); |
262 | |
263 | return g_test_run (); |
264 | } |
265 | |