GnuCash  5.6-150-g038405b370+
unittest-support.c
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 
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
56 {
57  g_free (err->log_domain);
58  g_free (err->msg);
59  g_slice_free (TestErrorStruct, err);
60 }
61 
62 GSList*
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*
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
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
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*
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
135 {
136  message_queue = g_list_append (message_queue, error);
137 }
138 
139 void
140 test_clear_error_list (void)
141 {
142  g_list_free (message_queue);
143  message_queue = NULL;
144 }
145 
146 
147 gboolean
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
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
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
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
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_true (log_level ^ G_LOG_FLAG_FATAL);
223  return FALSE;
224  }
225  if (hits)
226  ++(tdata->hits);
227  return FALSE;
228 
229 }
230 
231 gboolean
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_true (log_level ^ G_LOG_FLAG_FATAL);
246  return FALSE;
247  }
248  ++(tdata->hits);
249  return FALSE;
250 }
251 
252 gboolean
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
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
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_true (log_level ^ G_LOG_FLAG_FATAL);
276  return FALSE;
277 }
278 
279 void
280 test_set_called( const gboolean val )
281 {
282  tdata.called = val;
283 }
284 
285 gboolean
287 {
288  const gboolean called = tdata.called;
289  tdata.called = FALSE;
290  return called;
291 }
292 
293 void
294 test_set_data( const gpointer val )
295 {
296  tdata.data = val;
297 }
298 
299 gpointer
301 {
302  const gpointer data = tdata.data;
303  tdata.data = NULL;
304  return data;
305 }
306 
307 void
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
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_true (signal->event_data == event_data);
334  signal->hits += 1;
335  }
336 }
337 
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
353 {
354  _TestSignal *sig = (_TestSignal *)sigp;
355  qof_event_unregister_handler (sig->hdlr);
356  g_slice_free (_TestSignal, sig);
357 }
358 
359 guint
361 {
362  _TestSignal *sig = (_TestSignal *)sigp;
363  return sig->hits;
364 }
365 
366 static void
367 notify_destroy (gpointer pdata, GObject *obj)
368 {
369  gboolean *data = (gboolean*)pdata;
370  if (! (*data)) *data = TRUE;
371 }
372 
373 gboolean
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 }
gboolean test_list_handler(const char *log_domain, GLogLevelFlags log_level, const gchar *msg, gpointer user_data)
Checks received errors against the list created by test_add_error.
Macros and logging-capture functions to ease writing GLib-testing based unit tests.
void test_free_log_handler(gpointer item)
Clears all the log handlers.
gboolean test_checked_handler(const char *log_domain, GLogLevelFlags log_level, const gchar *msg, gpointer user_data)
Check the user_data against the actual error and assert on any differences.
Holds a handler instance with its TestErrorStruct, handler id, and whether it&#39;s a list handler...
gboolean test_checked_substring_handler(const char *log_domain, GLogLevelFlags log_level, const gchar *msg, gpointer user_data)
Check that the user_data error message is a substring of the actual error otherwise assert...
guint test_signal_return_hits(TestSignal sig)
gets the number of times the TestSignal has been called.
void test_set_data(gpointer data)
Set the test data pointer with the what you expect your mock to be called with.
gboolean test_log_handler(const char *log_domain, GLogLevelFlags log_level, const gchar *msg, gpointer user_data)
Just print the log message.
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
Definition: qofevent.cpp:73
void test_signal_free(TestSignal sig)
Free a test signal.
gboolean test_object_checked_destroy(GObject *obj)
Unrefs obj and returns true if its finalize method was called.
TestErrorStruct * test_error_struct_new(const char *log_domain, const GLogLevelFlags log_level, const char *msg)
Convenience function to create an error struct.
gint QofEventId
Define the type of events allowed.
Definition: qofevent.h:45
gboolean test_reset_called(void)
Destructively tests (meaning that it resets called to FALSE) and returns the value of called...
gboolean test_null_handler(const char *log_domain, GLogLevelFlags log_level, const gchar *msg, gpointer user_data)
Just returns FALSE or suppresses the message regardless of what the error is.
gboolean test_list_substring_handler(const char *log_domain, GLogLevelFlags log_level, const gchar *msg, gpointer user_data)
Checks received errors against the list created by test_add_error.
void test_free(gpointer data)
A handy function to use to free memory from lists of simple pointers.
gpointer test_reset_data(void)
Destructively retrieves the test data pointer.
Struct to pass as user_data for the handlers.
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
Definition: qofevent.cpp:103
GSList * test_log_set_fatal_handler(GSList *list, TestErrorStruct *error, GLogFunc handler)
Set a log handler and add it to a GList for removal at teardown; also set the fatal handler so that t...
GSList * test_log_set_handler(GSList *list, TestErrorStruct *error, GLogFunc handler)
Set a log handler and add it to a GList for removal at teardown.
void test_error_struct_free(TestErrorStruct *)
Free a TestErrorStruct created with test_error_struct_new.
TestSignal test_signal_new(QofInstance *entity, QofEventId eventType, gpointer event_data)
Create a test signal.
void test_set_called(const gboolean val)
Call this from a mock object to indicate that the mock has in fact been called.
gpointer TestSignal
TestSignal is an opaque struct used to mock handling signals emitted by functions-under-test.
void test_add_error(TestErrorStruct *error)
Maintains an internal list of TestErrorStructs which are each checked by the list handler...