1 /********************************************************************
2  * unittest-support.c: Support structures for GLib Unit Testing     *
3  * Copyright 2011-13 John Ralls <jralls@ceridwen.us>		    *
4  * Copyright 2011 Muslim Chochlov <muslim.chochlov@gmail.com>       *
5  *                                                                  *
6  * This program is free software; you can redistribute it and/or    *
7  * modify it under the terms of the GNU General Public License as   *
8  * published by the Free Software Foundation; either version 2 of   *
9  * the License, or (at your option) any later version.              *
10  *                                                                  *
11  * This program 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    *
14  * GNU General Public License for more details.                     *
15  *                                                                  *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact:                        *
18  *                                                                  *
19  * Free Software Foundation           Voice:  +1-617-542-5942       *
20  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
21  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
22 ********************************************************************/
23 
24 #include <config.h>
25 #include <glib/gprintf.h>
26 #include "unittest-support.h"
27 
28 typedef struct
29 {
30     gpointer data;
31     gboolean called;
32     char *msg;
33 } TestStruct;
34 
35 static TestStruct tdata;
36 static gboolean
37 test_checked_nohit_handler (const char *log_domain, GLogLevelFlags log_level,
38                             const gchar *msg, gpointer user_data);
39 static gboolean
40 test_list_nohit_handler (const char *log_domain, GLogLevelFlags log_level,
41                          const gchar *msg, gpointer user_data);
42 
43 TestErrorStruct*
test_error_struct_new(const char * log_domain,GLogLevelFlags log_level,const char * msg)44 test_error_struct_new (const char *log_domain, GLogLevelFlags log_level,
45                        const char *msg)
46 {
47     TestErrorStruct *err = g_slice_new0 (TestErrorStruct);
48     err->log_domain = g_strdup (log_domain);
49     err->log_level = log_level;
50     err->msg = g_strdup (msg);
51     return err;
52 }
53 
54 void
test_error_struct_free(TestErrorStruct * err)55 test_error_struct_free (TestErrorStruct *err)
56 {
57     g_free (err->log_domain);
58     g_free (err->msg);
59     g_slice_free (TestErrorStruct, err);
60 }
61 
62 GSList*
test_log_set_handler(GSList * list,TestErrorStruct * error,GLogFunc handler)63 test_log_set_handler (GSList *list, TestErrorStruct *error, GLogFunc handler)
64 {
65     TestLogHandler *hdlr = g_slice_new0 (TestLogHandler);
66     hdlr->error = error;
67     hdlr->handler = g_log_set_handler (error->log_domain, error->log_level,
68                                        handler, error);
69     return g_slist_prepend (list, hdlr);
70 }
71 
72 GSList*
test_log_set_fatal_handler(GSList * list,TestErrorStruct * error,GLogFunc handler)73 test_log_set_fatal_handler (GSList *list, TestErrorStruct *error,
74                             GLogFunc handler)
75 {
76     TestLogHandler *hdlr = g_slice_new0 (TestLogHandler);
77     GTestLogFatalFunc f_hdlr = handler == (GLogFunc)test_list_handler ?
78                                (GTestLogFatalFunc)test_list_nohit_handler :
79                                (GTestLogFatalFunc)test_checked_nohit_handler;
80     hdlr->error = error;
81     hdlr->handler = g_log_set_handler (error->log_domain, error->log_level,
82                                        handler, error);
83     g_test_log_set_fatal_handler (f_hdlr, error);
84     return g_slist_prepend (list, hdlr);
85 }
86 
87 void
test_free_log_handler(gpointer item)88 test_free_log_handler (gpointer item)
89 {
90     TestLogHandler *handler = (TestLogHandler*)item;
91     g_log_remove_handler (handler->error->log_domain, handler->handler);
92     test_error_struct_free (handler->error);
93     g_slice_free (TestLogHandler, handler);
94 }
95 
96 gboolean
test_null_handler(const char * log_domain,GLogLevelFlags log_level,const gchar * msg,gpointer user_data)97 test_null_handler (const char *log_domain, GLogLevelFlags log_level,
98                    const gchar *msg, gpointer user_data )
99 {
100     //Silent, remember?
101 
102     return FALSE;
103 }
104 
105 static gchar*
test_log_level(GLogLevelFlags flags)106 test_log_level (GLogLevelFlags flags)
107 {
108     const gchar *message[] = {"RECURSIVE", "FATAL", "ERROR", "CRITICAL",
109                               "WARNING", "MESSAGE", "INFO",  "DEBUG"
110                              };
111     guint i = 0, last = 0, max_bit = 7;
112     gchar *msg = NULL;
113 
114     for (i = 0; i <= max_bit; i++)
115         if (flags & 1 << i)
116         {
117             gchar *tmp_msg = msg;
118             gchar *sep = (last < 2 ? " " : "|");
119             last = i;
120             msg = (tmp_msg ? g_strjoin (sep, tmp_msg, message[i], NULL)
121                    : g_strdup (message[i]));
122             if (tmp_msg)
123                 g_free (tmp_msg);
124         }
125 
126     if (msg == NULL)
127         msg = g_strdup ("");
128     return msg;
129 }
130 
131 static GList *message_queue = NULL;
132 
133 void
test_add_error(TestErrorStruct * error)134 test_add_error (TestErrorStruct *error)
135 {
136     message_queue = g_list_append (message_queue, error);
137 }
138 
139 void
test_clear_error_list(void)140 test_clear_error_list (void)
141 {
142     g_list_free (message_queue);
143     message_queue = NULL;
144 }
145 
146 
147 gboolean
test_list_substring_handler(const char * log_domain,GLogLevelFlags log_level,const gchar * msg,gpointer user_data)148 test_list_substring_handler (const char *log_domain, GLogLevelFlags log_level,
149                       const gchar *msg, gpointer user_data)
150 {
151     GList *list = g_list_first (message_queue);
152     const guint fatal = G_LOG_FLAG_FATAL;
153     while (list)
154     {
155         TestErrorStruct *error = (TestErrorStruct*)list->data;
156         if (!g_strcmp0 (log_domain, error->log_domain)
157                 && ((log_level | fatal) == (error->log_level | fatal))
158                 && g_strrstr (msg, error->msg))
159         {
160             ++(error->hits);
161             return FALSE;
162         }
163         list = g_list_next (list);
164     }
165     /* No list or no matches, fall through */
166     return test_checked_substring_handler (log_domain, log_level, msg, user_data);
167 }
168 
169 static gboolean
do_test_list_handler(const char * log_domain,GLogLevelFlags log_level,const gchar * msg,gpointer user_data,gboolean hits)170 do_test_list_handler (const char *log_domain, GLogLevelFlags log_level,
171                       const gchar *msg, gpointer user_data, gboolean hits)
172 {
173     GList *list = g_list_first (message_queue);
174     const guint fatal = G_LOG_FLAG_FATAL;
175 
176     while (list)
177     {
178         TestErrorStruct *error = (TestErrorStruct*)list->data;
179         if (!g_strcmp0 (log_domain, error->log_domain)
180                 && ((log_level | fatal) == (error->log_level | fatal))
181                 && !g_strcmp0 (msg, error->msg))
182         {
183             if (hits)
184                 ++(error->hits);
185             return FALSE;
186         }
187         list = g_list_next (list);
188     }
189     /* No list or no matches, fall through */
190     return test_checked_handler (log_domain, log_level, msg, user_data);
191 }
192 
193 gboolean
test_list_handler(const char * log_domain,GLogLevelFlags log_level,const gchar * msg,gpointer user_data)194 test_list_handler (const char *log_domain, GLogLevelFlags log_level,
195                    const gchar *msg, gpointer user_data)
196 {
197     return do_test_list_handler (log_domain, log_level, msg, user_data, TRUE);
198 }
199 
200 gboolean
test_list_nohit_handler(const char * log_domain,GLogLevelFlags log_level,const gchar * msg,gpointer user_data)201 test_list_nohit_handler (const char *log_domain, GLogLevelFlags log_level,
202                          const gchar *msg, gpointer user_data)
203 {
204     return do_test_list_handler (log_domain, log_level, msg, user_data, FALSE);
205 }
206 
207 static gboolean
do_test_checked_handler(const char * log_domain,GLogLevelFlags log_level,const gchar * msg,gpointer user_data,gboolean hits)208 do_test_checked_handler (const char *log_domain, GLogLevelFlags log_level,
209                          const gchar *msg, gpointer user_data, gboolean hits)
210 {
211     TestErrorStruct *tdata = (TestErrorStruct*)user_data;
212 
213     if ((tdata == NULL)
214             || (tdata->log_domain != NULL
215                 && g_strcmp0 (tdata->log_domain, log_domain))
216             || (tdata->log_level && tdata->log_level != log_level)
217             || (tdata->msg && g_strcmp0 (tdata->msg, msg)))
218     {
219         gchar *level = test_log_level (log_level);
220         g_printf ( "<%s> (%s) %s\n", level, log_domain, msg);
221         g_free (level);
222         g_assert (log_level ^ G_LOG_FLAG_FATAL);
223         return FALSE;
224     }
225     if (hits)
226         ++(tdata->hits);
227     return FALSE;
228 
229 }
230 
231 gboolean
test_checked_substring_handler(const char * log_domain,GLogLevelFlags log_level,const gchar * msg,gpointer user_data)232 test_checked_substring_handler (const char *log_domain, GLogLevelFlags log_level,
233                                          const gchar *msg, gpointer user_data)
234 {
235     TestErrorStruct *tdata = (TestErrorStruct*)user_data;
236     if ((tdata == NULL)
237             || (tdata->log_domain != NULL
238                 && g_strcmp0 (log_domain, tdata->log_domain))
239             || (tdata->log_level && tdata->log_level != log_level)
240             || (tdata->msg && !g_strrstr (msg, tdata->msg)))
241     {
242         gchar *level = test_log_level (log_level);
243         g_printf ( "<%s> (%s) %s\n", level, log_domain, msg);
244         g_free (level);
245         g_assert (log_level ^ G_LOG_FLAG_FATAL);
246         return FALSE;
247     }
248     ++(tdata->hits);
249     return FALSE;
250 }
251 
252 gboolean
test_checked_handler(const char * log_domain,GLogLevelFlags log_level,const gchar * msg,gpointer user_data)253 test_checked_handler (const char *log_domain, GLogLevelFlags log_level,
254                       const gchar *msg, gpointer user_data )
255 {
256     return do_test_checked_handler (log_domain, log_level, msg,
257                                     user_data, TRUE);
258 }
259 
260 static gboolean
test_checked_nohit_handler(const char * log_domain,GLogLevelFlags log_level,const gchar * msg,gpointer user_data)261 test_checked_nohit_handler (const char *log_domain, GLogLevelFlags log_level,
262                             const gchar *msg, gpointer user_data )
263 {
264     return do_test_checked_handler (log_domain, log_level, msg,
265                                     user_data, FALSE);
266 }
267 
268 gboolean
test_log_handler(const char * log_domain,GLogLevelFlags log_level,const gchar * msg,gpointer user_data)269 test_log_handler (const char *log_domain, GLogLevelFlags log_level,
270                   const gchar *msg, gpointer user_data )
271 {
272     gchar *level = test_log_level (log_level);
273     g_printf ( "<%s> (%s) %s\n", level, log_domain, msg);
274     g_free (level);
275     g_assert (log_level ^ G_LOG_FLAG_FATAL);
276     return FALSE;
277 }
278 
279 void
test_set_called(const gboolean val)280 test_set_called( const gboolean val )
281 {
282     tdata.called = val;
283 }
284 
285 gboolean
test_reset_called(void)286 test_reset_called( void )
287 {
288     const gboolean called  = tdata.called;
289     tdata.called = FALSE;
290     return called;
291 }
292 
293 void
test_set_data(const gpointer val)294 test_set_data( const gpointer val )
295 {
296     tdata.data = val;
297 }
298 
299 gpointer
test_reset_data(void)300 test_reset_data( void )
301 {
302     const gpointer data  = tdata.data;
303     tdata.data = NULL;
304     return data;
305 }
306 
307 void
test_free(gpointer data)308 test_free( gpointer data )
309 {
310     if (!data) return;
311     g_free(data);
312 }
313 
314 
315 typedef struct
316 {
317     QofInstance *entity;
318     QofEventId event_type;
319     gpointer event_data;
320     gint hdlr;
321     guint hits;
322 } _TestSignal;
323 
324 static void
mock_signal_handler(QofInstance * entity,QofEventId event_type,gpointer handler_data,gpointer event_data)325 mock_signal_handler (QofInstance *entity, QofEventId event_type,
326                      gpointer handler_data, gpointer event_data)
327 {
328     _TestSignal *signal = (_TestSignal*)handler_data;
329     if ((signal->entity == entity || signal->entity == NULL)
330             && signal->event_type == event_type)
331     {
332         if (signal->event_data)
333             g_assert (signal->event_data == event_data);
334         signal->hits += 1;
335     }
336 }
337 
338 TestSignal
test_signal_new(QofInstance * entity,QofEventId event_type,gpointer event_data)339 test_signal_new (QofInstance *entity, QofEventId event_type,
340                  gpointer event_data)
341 {
342     _TestSignal *sig = g_slice_new (_TestSignal);
343     sig->entity = entity;
344     sig->event_type = event_type;
345     sig->event_data = event_data;
346     sig->hits = 0;
347     sig->hdlr = qof_event_register_handler (mock_signal_handler, (gpointer)sig);
348     return (TestSignal)sig;
349 }
350 
351 void
test_signal_free(TestSignal sigp)352 test_signal_free (TestSignal sigp)
353 {
354     _TestSignal *sig = (_TestSignal *)sigp;
355     qof_event_unregister_handler (sig->hdlr);
356     g_slice_free (_TestSignal, sig);
357 }
358 
359 guint
test_signal_return_hits(TestSignal sigp)360 test_signal_return_hits (TestSignal sigp)
361 {
362     _TestSignal *sig = (_TestSignal *)sigp;
363     return sig->hits;
364 }
365 
366 static void
notify_destroy(gpointer pdata,GObject * obj)367 notify_destroy (gpointer pdata, GObject *obj)
368 {
369     gboolean *data = (gboolean*)pdata;
370     if (! (*data)) *data = TRUE;
371 }
372 
373 gboolean
test_object_checked_destroy(GObject * obj)374 test_object_checked_destroy (GObject *obj)
375 {
376     gboolean is_destroyed = FALSE;
377     if (!obj || ! G_IS_OBJECT (obj)) return FALSE;
378     g_object_weak_ref (obj, notify_destroy, &is_destroyed);
379     g_object_unref (obj);
380     return is_destroyed;
381 }
382