]> Pileus Git - ~andy/gtk/blob - gtk/tests/gtktreemodelrefcount.c
tests: Relax a refcount comparison check
[~andy/gtk] / gtk / tests / gtktreemodelrefcount.c
1 /* gtktreemodelrefcount.c
2  * Copyright (C) 2011  Kristian Rietveld <kris@gtk.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21 #include "gtktreemodelrefcount.h"
22
23
24 /* The purpose of this GtkTreeModel is to keep record of the reference count
25  * of each node.  The reference count does not effect the functioning of
26  * the model in any way.  Because this model is a subclass of GtkTreeStore,
27  * the GtkTreeStore API should be used to add to and remove nodes from
28  * this model.  We depend on the iter format of GtkTreeStore, which means
29  * that this model needs to be revised in case the iter format of
30  * GtkTreeStore is modified.  Currently, we make use of the fact that
31  * the value stored in the user_data field is unique for each node.
32  */
33
34 struct _GtkTreeModelRefCountPrivate
35 {
36   GHashTable *node_hash;
37 };
38
39 typedef struct
40 {
41   int ref_count;
42 }
43 NodeInfo;
44
45
46 static void      gtk_tree_model_ref_count_tree_model_init (GtkTreeModelIface *iface);
47 static void      gtk_tree_model_ref_count_finalize        (GObject           *object);
48
49 static NodeInfo *node_info_new                            (void);
50 static void      node_info_free                           (NodeInfo          *info);
51
52 /* GtkTreeModel interface */
53 static void      gtk_tree_model_ref_count_ref_node        (GtkTreeModel      *model,
54                                                            GtkTreeIter       *iter);
55 static void      gtk_tree_model_ref_count_unref_node      (GtkTreeModel      *model,
56                                                            GtkTreeIter       *iter);
57
58
59 G_DEFINE_TYPE_WITH_CODE (GtkTreeModelRefCount, gtk_tree_model_ref_count, GTK_TYPE_TREE_STORE,
60                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
61                                                 gtk_tree_model_ref_count_tree_model_init))
62
63 static void
64 gtk_tree_model_ref_count_init (GtkTreeModelRefCount *ref_model)
65 {
66   ref_model->priv = G_TYPE_INSTANCE_GET_PRIVATE (ref_model,
67                                                  GTK_TYPE_TREE_MODEL_REF_COUNT,
68                                                  GtkTreeModelRefCountPrivate);
69
70   ref_model->priv->node_hash = g_hash_table_new_full (g_direct_hash,
71                                                       g_direct_equal,
72                                                       NULL,
73                                                       (GDestroyNotify)node_info_free);
74 }
75
76 static void
77 gtk_tree_model_ref_count_class_init (GtkTreeModelRefCountClass *ref_model_class)
78 {
79   GObjectClass *object_class;
80
81   object_class = (GObjectClass *) ref_model_class;
82
83   object_class->finalize = gtk_tree_model_ref_count_finalize;
84
85   g_type_class_add_private (object_class, sizeof (GtkTreeModelRefCountPrivate));
86 }
87
88 static void
89 gtk_tree_model_ref_count_tree_model_init (GtkTreeModelIface *iface)
90 {
91   iface->ref_node = gtk_tree_model_ref_count_ref_node;
92   iface->unref_node = gtk_tree_model_ref_count_unref_node;
93 }
94
95 static void
96 gtk_tree_model_ref_count_finalize (GObject *object)
97 {
98   GtkTreeModelRefCount *ref_model = GTK_TREE_MODEL_REF_COUNT (object);
99
100   if (ref_model->priv->node_hash)
101     {
102       g_hash_table_destroy (ref_model->priv->node_hash);
103       ref_model->priv->node_hash = NULL;
104     }
105
106   G_OBJECT_CLASS (gtk_tree_model_ref_count_parent_class)->finalize (object);
107 }
108
109
110 static NodeInfo *
111 node_info_new (void)
112 {
113   NodeInfo *info = g_slice_new (NodeInfo);
114   info->ref_count = 0;
115
116   return info;
117 }
118
119 static void
120 node_info_free (NodeInfo *info)
121 {
122   g_slice_free (NodeInfo, info);
123 }
124
125 static void
126 gtk_tree_model_ref_count_ref_node (GtkTreeModel *model,
127                                    GtkTreeIter  *iter)
128 {
129   NodeInfo *info;
130   GtkTreeModelRefCount *ref_model = GTK_TREE_MODEL_REF_COUNT (model);
131
132   info = g_hash_table_lookup (ref_model->priv->node_hash, iter->user_data);
133   if (!info)
134     {
135       info = node_info_new ();
136
137       g_hash_table_insert (ref_model->priv->node_hash, iter->user_data, info);
138     }
139
140   info->ref_count++;
141 }
142
143 static void
144 gtk_tree_model_ref_count_unref_node (GtkTreeModel *model,
145                                      GtkTreeIter  *iter)
146 {
147   NodeInfo *info;
148   GtkTreeModelRefCount *ref_model = GTK_TREE_MODEL_REF_COUNT (model);
149
150   info = g_hash_table_lookup (ref_model->priv->node_hash, iter->user_data);
151   g_assert (info != NULL);
152   g_assert (info->ref_count > 0);
153
154   info->ref_count--;
155 }
156
157
158 GtkTreeModel *
159 gtk_tree_model_ref_count_new (void)
160 {
161   GtkTreeModel *retval;
162
163   retval = g_object_new (gtk_tree_model_ref_count_get_type (), NULL);
164
165   return retval;
166 }
167
168 static void
169 dump_iter (GtkTreeModelRefCount *ref_model,
170            GtkTreeIter          *iter)
171 {
172   gchar *path_str;
173   NodeInfo *info;
174   GtkTreePath *path;
175
176   path = gtk_tree_model_get_path (GTK_TREE_MODEL (ref_model), iter);
177   path_str = gtk_tree_path_to_string (path);
178   gtk_tree_path_free (path);
179
180   info = g_hash_table_lookup (ref_model->priv->node_hash, iter->user_data);
181   if (!info)
182     g_print ("%-16s ref_count=0\n", path_str);
183   else
184     g_print ("%-16s ref_count=%d\n", path_str, info->ref_count);
185
186   g_free (path_str);
187 }
188
189 static void
190 gtk_tree_model_ref_count_dump_recurse (GtkTreeModelRefCount *ref_model,
191                                        GtkTreeIter          *iter)
192 {
193   do
194     {
195       GtkTreeIter child;
196
197       dump_iter (ref_model, iter);
198
199       if (gtk_tree_model_iter_children (GTK_TREE_MODEL (ref_model),
200                                         &child, iter))
201         gtk_tree_model_ref_count_dump_recurse (ref_model, &child);
202     }
203   while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ref_model), iter));
204 }
205
206 void
207 gtk_tree_model_ref_count_dump (GtkTreeModelRefCount *ref_model)
208 {
209   GtkTreeIter iter;
210
211   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ref_model), &iter))
212     return;
213
214   gtk_tree_model_ref_count_dump_recurse (ref_model, &iter);
215 }
216
217 static gboolean
218 check_iter (GtkTreeModelRefCount *ref_model,
219             GtkTreeIter          *iter,
220             gint                  expected_ref_count,
221             gboolean              may_assert)
222 {
223   NodeInfo *info;
224
225   if (may_assert)
226     g_assert (gtk_tree_store_iter_is_valid (GTK_TREE_STORE (ref_model), iter));
227
228   info = g_hash_table_lookup (ref_model->priv->node_hash, iter->user_data);
229   if (!info)
230     {
231       if (expected_ref_count == 0)
232         return TRUE;
233       else
234         {
235           if (may_assert)
236             g_error ("Expected ref count %d, but node has never been referenced.\n", expected_ref_count);
237           return FALSE;
238         }
239     }
240
241   if (may_assert)
242     {
243       if (expected_ref_count == 0)
244         g_assert_cmpint (expected_ref_count, ==, info->ref_count);
245       else
246         g_assert_cmpint (expected_ref_count, <=, info->ref_count);
247     }
248
249   return expected_ref_count == info->ref_count;
250 }
251
252 gboolean
253 gtk_tree_model_ref_count_check_level (GtkTreeModelRefCount *ref_model,
254                                       GtkTreeIter          *parent,
255                                       gint                  expected_ref_count,
256                                       gboolean              recurse,
257                                       gboolean              may_assert)
258 {
259   GtkTreeIter iter;
260
261   if (!gtk_tree_model_iter_children (GTK_TREE_MODEL (ref_model),
262                                      &iter, parent))
263     return TRUE;
264
265   do
266     {
267       if (!check_iter (ref_model, &iter, expected_ref_count, may_assert))
268         return FALSE;
269
270       if (recurse &&
271           gtk_tree_model_iter_has_child (GTK_TREE_MODEL (ref_model), &iter))
272         {
273           if (!gtk_tree_model_ref_count_check_level (ref_model, &iter,
274                                                      expected_ref_count,
275                                                      recurse, may_assert))
276             return FALSE;
277         }
278     }
279   while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ref_model), &iter));
280
281   return TRUE;
282 }
283
284 gboolean
285 gtk_tree_model_ref_count_check_node (GtkTreeModelRefCount *ref_model,
286                                      GtkTreeIter          *iter,
287                                      gint                  expected_ref_count,
288                                      gboolean              may_assert)
289 {
290   return check_iter (ref_model, iter, expected_ref_count, may_assert);
291 }