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
prepend(GListStore * store,guint number,guint step)61 prepend (GListStore *store,
62          guint       number,
63          guint       step)
64 {
65   GObject *object;
66 
67   /* 0 cannot be differentiated from NULL, so don't use it */
68   g_assert_cmpint (number, !=, 0);
69 
70   if (step / 10)
71     object = G_OBJECT (new_store (number - 9 * step / 10, number, step / 10));
72   else
73     object = g_object_new (G_TYPE_OBJECT, NULL);
74   g_object_set_qdata (object, number_quark, GUINT_TO_POINTER (number));
75   g_list_store_insert (store, 0, object);
76   g_object_unref (object);
77 }
78 
79 #define assert_model(model, expected) G_STMT_START{ \
80   char *s = model_to_string (G_LIST_MODEL (model)); \
81   if (!g_str_equal (s, expected)) \
82      g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
83          #model " == " #expected, s, "==", expected); \
84   g_free (s); \
85 }G_STMT_END
86 
87 #define assert_changes(model, expected) G_STMT_START{ \
88   GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \
89   if (!g_str_equal (changes->str, expected)) \
90      g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
91          #model " == " #expected, changes->str, "==", expected); \
92   g_string_set_size (changes, 0); \
93 }G_STMT_END
94 
95 static GListStore *
new_empty_store(void)96 new_empty_store (void)
97 {
98   return g_list_store_new (G_TYPE_OBJECT);
99 }
100 
101 static GListStore *
new_store(guint start,guint end,guint step)102 new_store (guint start,
103            guint end,
104            guint step)
105 {
106   GListStore *store = new_empty_store ();
107   guint i;
108 
109   for (i = start; i <= end; i += step)
110     prepend (store, i, step);
111 
112   return store;
113 }
114 
115 static void
items_changed(GListModel * model,guint position,guint removed,guint added,GString * changes)116 items_changed (GListModel *model,
117                guint       position,
118                guint       removed,
119                guint       added,
120                GString    *changes)
121 {
122   g_assert_true (removed != 0 || added != 0);
123 
124   if (changes->len)
125     g_string_append (changes, ", ");
126 
127   if (removed == 1 && added == 0)
128     {
129       g_string_append_printf (changes, "-%u", position);
130     }
131   else if (removed == 0 && added == 1)
132     {
133       g_string_append_printf (changes, "+%u", position);
134     }
135   else
136     {
137       g_string_append_printf (changes, "%u", position);
138       if (removed > 0)
139         g_string_append_printf (changes, "-%u", removed);
140       if (added > 0)
141         g_string_append_printf (changes, "+%u", added);
142     }
143 }
144 
145 static void
free_changes(gpointer data)146 free_changes (gpointer data)
147 {
148   GString *changes = data;
149 
150   /* all changes must have been checked via assert_changes() before */
151   g_assert_cmpstr (changes->str, ==, "");
152 
153   g_string_free (changes, TRUE);
154 }
155 
156 static GListModel *
create_sub_model_cb(gpointer item,gpointer unused)157 create_sub_model_cb (gpointer item,
158                      gpointer unused)
159 {
160   if (G_IS_LIST_MODEL (item))
161     return g_object_ref (item);
162 
163   return NULL;
164 }
165 
166 static GtkTreeListModel *
new_model(guint size,gboolean expanded)167 new_model (guint    size,
168            gboolean expanded)
169 {
170   GtkTreeListModel *tree;
171   GString *changes;
172 
173   tree = gtk_tree_list_model_new (G_LIST_MODEL (new_store (size, size, size)), TRUE, expanded, create_sub_model_cb, NULL, NULL);
174   changes = g_string_new ("");
175   g_object_set_qdata_full (G_OBJECT(tree), changes_quark, changes, free_changes);
176   g_signal_connect (tree, "items-changed", G_CALLBACK (items_changed), changes);
177 
178   return tree;
179 }
180 
181 static void
test_expand(void)182 test_expand (void)
183 {
184   GtkTreeListModel *tree = new_model (100, FALSE);
185   guint i;
186 
187   assert_model (tree, "100");
188 
189   for (i = g_list_model_get_n_items (G_LIST_MODEL (tree)); i > 0; i--)
190     {
191       GtkTreeListRow *row = gtk_tree_list_model_get_row (tree, i - 1);
192       gtk_tree_list_row_set_expanded (row, TRUE);
193       g_object_unref (row);
194     }
195   assert_model (tree, "100 100 90 80 70 60 50 40 30 20 10");
196   assert_changes (tree, "1+10");
197 
198   for (i = g_list_model_get_n_items (G_LIST_MODEL (tree)); i > 0; i--)
199     {
200       GtkTreeListRow *row = gtk_tree_list_model_get_row (tree, i - 1);
201       gtk_tree_list_row_set_expanded (row, TRUE);
202       g_object_unref (row);
203     }
204   assert_model (tree, "100 100 100 99 98 97 96 95 94 93 92 91 90 90 89 88 87 86 85 84 83 82 81 80 80 79 78 77 76 75 74 73 72 71 70 70 69 68 67 66 65 64 63 62 61 60 60 59 58 57 56 55 54 53 52 51 50 50 49 48 47 46 45 44 43 42 41 40 40 39 38 37 36 35 34 33 32 31 30 30 29 28 27 26 25 24 23 22 21 20 20 19 18 17 16 15 14 13 12 11 10 10 9 8 7 6 5 4 3 2 1");
205   assert_changes (tree, "11+10, 10+10, 9+10, 8+10, 7+10, 6+10, 5+10, 4+10, 3+10, 2+10");
206 
207   for (i = g_list_model_get_n_items (G_LIST_MODEL (tree)); i > 0; i--)
208     {
209       GtkTreeListRow *row = gtk_tree_list_model_get_row (tree, i - 1);
210       gtk_tree_list_row_set_expanded (row, TRUE);
211       g_object_unref (row);
212     }
213   assert_model (tree, "100 100 100 99 98 97 96 95 94 93 92 91 90 90 89 88 87 86 85 84 83 82 81 80 80 79 78 77 76 75 74 73 72 71 70 70 69 68 67 66 65 64 63 62 61 60 60 59 58 57 56 55 54 53 52 51 50 50 49 48 47 46 45 44 43 42 41 40 40 39 38 37 36 35 34 33 32 31 30 30 29 28 27 26 25 24 23 22 21 20 20 19 18 17 16 15 14 13 12 11 10 10 9 8 7 6 5 4 3 2 1");
214   assert_changes (tree, "");
215 
216   g_object_unref (tree);
217 }
218 
219 static void
test_remove_some(void)220 test_remove_some (void)
221 {
222   GtkTreeListModel *tree = new_model (100, TRUE);
223   gpointer item;
224 
225   assert_model (tree, "100 100 100 99 98 97 96 95 94 93 92 91 90 90 89 88 87 86 85 84 83 82 81 80 80 79 78 77 76 75 74 73 72 71 70 70 69 68 67 66 65 64 63 62 61 60 60 59 58 57 56 55 54 53 52 51 50 50 49 48 47 46 45 44 43 42 41 40 40 39 38 37 36 35 34 33 32 31 30 30 29 28 27 26 25 24 23 22 21 20 20 19 18 17 16 15 14 13 12 11 10 10 9 8 7 6 5 4 3 2 1");
226   assert_changes (tree, "");
227 
228   item = g_list_model_get_item (G_LIST_MODEL (tree), 1);
229   g_assert_true (G_IS_LIST_MODEL (item));
230   g_list_store_remove (item, 3);
231   assert_model (tree, "100 100 100 99 98 96 95 94 93 92 91 90 90 89 88 87 86 85 84 83 82 81 80 80 79 78 77 76 75 74 73 72 71 70 70 69 68 67 66 65 64 63 62 61 60 60 59 58 57 56 55 54 53 52 51 50 50 49 48 47 46 45 44 43 42 41 40 40 39 38 37 36 35 34 33 32 31 30 30 29 28 27 26 25 24 23 22 21 20 20 19 18 17 16 15 14 13 12 11 10 10 9 8 7 6 5 4 3 2 1");
232   assert_changes (tree, "-5");
233 
234   item = g_list_model_get_item (G_LIST_MODEL (tree), 0);
235   g_assert_true (G_IS_LIST_MODEL (item));
236   g_list_store_remove (item, 3);
237   assert_model (tree, "100 100 100 99 98 96 95 94 93 92 91 90 90 89 88 87 86 85 84 83 82 81 80 80 79 78 77 76 75 74 73 72 71 60 60 59 58 57 56 55 54 53 52 51 50 50 49 48 47 46 45 44 43 42 41 40 40 39 38 37 36 35 34 33 32 31 30 30 29 28 27 26 25 24 23 22 21 20 20 19 18 17 16 15 14 13 12 11 10 10 9 8 7 6 5 4 3 2 1");
238   assert_changes (tree, "33-11");
239 
240   item = g_list_model_get_item (G_LIST_MODEL (tree), 88);
241   g_assert_true (G_IS_LIST_MODEL (item));
242   g_list_store_remove (item, 9);
243   assert_model (tree, "100 100 100 99 98 96 95 94 93 92 91 90 90 89 88 87 86 85 84 83 82 81 80 80 79 78 77 76 75 74 73 72 71 60 60 59 58 57 56 55 54 53 52 51 50 50 49 48 47 46 45 44 43 42 41 40 40 39 38 37 36 35 34 33 32 31 30 30 29 28 27 26 25 24 23 22 21 20 20 19 18 17 16 15 14 13 12 11 10 10 9 8 7 6 5 4 3 2");
244   assert_changes (tree, "-98");
245 
246   item = g_list_model_get_item (G_LIST_MODEL (tree), 0);
247   g_assert_true (G_IS_LIST_MODEL (item));
248   g_list_store_remove (item, 8);
249   assert_model (tree, "100 100 100 99 98 96 95 94 93 92 91 90 90 89 88 87 86 85 84 83 82 81 80 80 79 78 77 76 75 74 73 72 71 60 60 59 58 57 56 55 54 53 52 51 50 50 49 48 47 46 45 44 43 42 41 40 40 39 38 37 36 35 34 33 32 31 30 30 29 28 27 26 25 24 23 22 21 20 20 19 18 17 16 15 14 13 12 11");
250   assert_changes (tree, "88-10");
251 
252   g_object_unref (tree);
253 }
254 
255 int
main(int argc,char * argv[])256 main (int argc, char *argv[])
257 {
258   (g_test_init) (&argc, &argv, NULL);
259   setlocale (LC_ALL, "C");
260 
261   number_quark = g_quark_from_static_string ("Hell and fire was spawned to be released.");
262   changes_quark = g_quark_from_static_string ("What did I see? Can I believe what I saw?");
263 
264   g_test_add_func ("/treelistmodel/expand", test_expand);
265   g_test_add_func ("/treelistmodel/remove_some", test_remove_some);
266 
267   return g_test_run ();
268 }
269