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
25static GQuark number_quark;
26
27static guint
28get_number (GObject *object)
29{
30 return GPOINTER_TO_UINT (g_object_get_qdata (object, number_quark));
31}
32
33static guint
34get_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
59static char *
60model_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
78static GListStore *
79new_store (guint start,
80 guint end,
81 guint step);
82
83static void
84add (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
126static GListStore *
127new_empty_store (void)
128{
129 return g_list_store_new (G_TYPE_OBJECT);
130}
131
132static GListStore *
133new_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
146static GListModel *
147new_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
158static GListModel *
159new_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
168static void
169test_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
185static GtkSorter *
186new_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
191static void
192test_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
221static void
222test_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
251int
252main (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

source code of gtk/testsuite/gtk/treesorter.c