GnuCash  5.6-150-g038405b370+
Data Structures
unittest-support.h File Reference

Macros and logging-capture functions to ease writing GLib-testing based unit tests. More...

#include <glib.h>
#include <qof.h>

Go to the source code of this file.

Data Structures

struct  TestErrorStruct
 Struct to pass as user_data for the handlers. More...
 
struct  TestLogHandler
 Holds a handler instance with its TestErrorStruct, handler id, and whether it's a list handler. More...
 

Macros

Unit Test Macros

These macros facilitate combining a suite name and a test name to make a path when registering a test function with the corresponding g_test_add and g_test_add_func.

Create a const char* suitename somewhere in the file and pass it as the first parameter, and the test name as the second. The remaining parameters are the same as in the underlying function.

#define GNC_TEST_ADD(suite, path, fixture, data, setup, test, teardown)
 Wraps gnc_test_add() for test functions needing a fixture. More...
 
#define GNC_TEST_ADD_FUNC(suite, path, test)
 Wraps gnc_test_add_func() for test functions which don't require a fixture. More...
 

Functions

Suppressing Expected Errors

Functions for suppressing expected errors during tests.

Pass

Note that you need to call both g_log_set_handler and g_test_log_set_fatal_handler to both avoid the assertion and suppress the error message. The callbacks work in either role, just cast them appropriately for the use.

To simplify the process a bit and make sure that everything gets cleaned up at the end of each test, add a GSList for handlers to your Fixture and set it to NULL in setup(), then call g_slist_free_full() on it with test_free_log_handler as the function. Create new TestErrorStruct instances with test_error_struct_new, and pass that along with your handler of choice to test_log_set_handler or test_log_set_fatal_handler. This is much simpler, as teardown will clean everything up for you and you need call only those functions. As an added bonus, the hit count won't be doubled as it is if you do everything by hand.

NB: If you have more than one fatal error in a test function be sure to use the test_list_handler: You can have only one fatal handler.

TestErrorStructtest_error_struct_new (const char *log_domain, const GLogLevelFlags log_level, const char *msg)
 Convenience function to create an error struct. More...
 
void test_error_struct_free (TestErrorStruct *)
 Free a TestErrorStruct created with test_error_struct_new. More...
 
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. More...
 
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 the test program doesn't abort for fatal log messages. More...
 
void test_free_log_handler (gpointer item)
 Clears all the log handlers. More...
 
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. More...
 
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. More...
 
gboolean test_log_handler (const char *log_domain, GLogLevelFlags log_level, const gchar *msg, gpointer user_data)
 Just print the log message. More...
 
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. More...
 
void test_add_error (TestErrorStruct *error)
 Maintains an internal list of TestErrorStructs which are each checked by the list handler. More...
 
void test_clear_error_list (void)
 
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. More...
 
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. More...
 
void test_set_called (const gboolean val)
 Call this from a mock object to indicate that the mock has in fact been called.
 
gboolean test_reset_called (void)
 Destructively tests (meaning that it resets called to FALSE) and returns the value of called.
 
void test_set_data (gpointer data)
 Set the test data pointer with the what you expect your mock to be called with.
 
gpointer test_reset_data (void)
 Destructively retrieves the test data pointer. More...
 
void test_free (gpointer data)
 A handy function to use to free memory from lists of simple pointers. More...
 

Test Signals

Test the emission of signals from objects.

Signals are used to coordinate the behavior of the GUI with events in other parts of the program.

#define test_signal_assert_hits(sig, hits)   g_assert_cmpint (test_signal_return_hits (sig), ==, hits)
 Convenience macro which wraps test_signal_return_hits with and equality assertion.
 
typedef gpointer TestSignal
 TestSignal is an opaque struct used to mock handling signals emitted by functions-under-test. More...
 
TestSignal test_signal_new (QofInstance *entity, QofEventId eventType, gpointer event_data)
 Create a test signal. More...
 
guint test_signal_return_hits (TestSignal sig)
 gets the number of times the TestSignal has been called.
 
void test_signal_free (TestSignal sig)
 Free a test signal.
 

Testing for object disposal

Sometimes we need to make sure that certain objects that we've created aren't leaking.

These functions can help.

#define test_destroy(obj)
 Ensures that a GObject is still alive at the time it's called and that it is finalized. More...
 
gboolean test_object_checked_destroy (GObject *obj)
 Unrefs obj and returns true if its finalize method was called.
 

Detailed Description

Macros and logging-capture functions to ease writing GLib-testing based unit tests.

Definition in file unittest-support.h.

Macro Definition Documentation

◆ GNC_TEST_ADD

#define GNC_TEST_ADD (   suite,
  path,
  fixture,
  data,
  setup,
  test,
  teardown 
)
Value:
{\
gchar *testpath = g_strdup_printf( "%s/%s", suite, path );\
g_test_add( testpath, fixture, data, setup, test, teardown );\
g_free( testpath );\
}

Wraps gnc_test_add() for test functions needing a fixture.

Definition at line 50 of file unittest-support.h.

◆ GNC_TEST_ADD_FUNC

#define GNC_TEST_ADD_FUNC (   suite,
  path,
  test 
)
Value:
{\
gchar *testpath = g_strdup_printf( "%s/%s", suite, path );\
g_test_add_func( testpath, test );\
g_free( testpath );\
}

Wraps gnc_test_add_func() for test functions which don't require a fixture.

Definition at line 61 of file unittest-support.h.

◆ test_destroy

#define test_destroy (   obj)
Value:
g_assert_true (obj != NULL && G_IS_OBJECT (obj)); \
g_assert_true (test_object_checked_destroy (G_OBJECT (obj)))
gboolean test_object_checked_destroy(GObject *obj)
Unrefs obj and returns true if its finalize method was called.

Ensures that a GObject is still alive at the time it's called and that it is finalized.

The first assertion will trigger if you pass it a pointer which isn't a GObject – which could be the case if the object has already been finalized. Then it calls test_object_checked_destroy() on it, asserting if the finalize method wasn't called (which indicates a leak).

Definition at line 351 of file unittest-support.h.

Typedef Documentation

◆ TestSignal

typedef gpointer TestSignal

TestSignal is an opaque struct used to mock handling signals emitted by functions-under-test.

It registers a handler and counts how many times it is called with the right instance and type. The struct is allocated using g_slice_new, and it registers a qof_event_handler; test_signal_free cleans up at the end of the test function (or sooner, if you want to reuse a TestSignal). If event_data isn't NULL, the mock signal handler will test that it matches the event_data passed with the signal and assert if it isn't the same object (pointer comparison). If the actual event data is a local variable, it won't be accessible, so the event_data passed to test_signal_new should be NULL to avoid the test.

Definition at line 299 of file unittest-support.h.

Function Documentation

◆ test_add_error()

void test_add_error ( TestErrorStruct error)

Maintains an internal list of TestErrorStructs which are each checked by the list handler.

If an error matches any entry on the list, test_list_handler will return FALSE, blocking the error from halting the program.

Call test_add_error for each TestErrorStruct to check against and test_clear_error_list when you no longer expect the errors.

Definition at line 134 of file unittest-support.c.

135 {
136  message_queue = g_list_append (message_queue, error);
137 }

◆ test_checked_handler()

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.

Displays the error (and asserts if G_LOG_FLAG_FATAL is TRUE) if NULL is passed as user_data, but a NULL or 0 value member matches anything.

Definition at line 253 of file unittest-support.c.

255 {
256  return do_test_checked_handler (log_domain, log_level, msg,
257  user_data, TRUE);
258 }

◆ test_checked_substring_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.

Displays the error (and asserts if G_LOG_FLAG_FATAL is TRUE) if NULL is passed as user_data, but a NULL or 0 value member matches anything.

Definition at line 232 of file unittest-support.c.

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 }
Struct to pass as user_data for the handlers.

◆ test_error_struct_free()

void test_error_struct_free ( TestErrorStruct )

Free a TestErrorStruct created with test_error_struct_new.

Parameters
errorThe TestErrorStruct to be freed

Definition at line 55 of file unittest-support.c.

56 {
57  g_free (err->log_domain);
58  g_free (err->msg);
59  g_slice_free (TestErrorStruct, err);
60 }
Struct to pass as user_data for the handlers.

◆ test_error_struct_new()

TestErrorStruct* test_error_struct_new ( const char *  log_domain,
const GLogLevelFlags  log_level,
const char *  msg 
)

Convenience function to create an error struct.

If you use this with test_set_log_handler it will get cleaned up at tesrdown, otherwise call test_error_free() at the end of your test function.

NB: If you need to change the message, be sure to free the old one and to allocate the new one on the stack.

Parameters
log_domainThe string representing the domain of the log message
log_levelThe GLogLevelFlags for the message
msgThe exact error message that the logger will emit
Returns
: A TestErrorStruct *

Definition at line 44 of file unittest-support.c.

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 }
Struct to pass as user_data for the handlers.

◆ test_free()

void test_free ( gpointer  data)

A handy function to use to free memory from lists of simple pointers.

Call g_list_free_full(list, (GDestroyNotify)*test_free).

Definition at line 308 of file unittest-support.c.

309 {
310  if (!data) return;
311  g_free(data);
312 }

◆ test_free_log_handler()

void test_free_log_handler ( gpointer  item)

Clears all the log handlers.

Pass this to g_slist_free() in teardown.

Definition at line 88 of file unittest-support.c.

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 }
Holds a handler instance with its TestErrorStruct, handler id, and whether it&#39;s a list handler...
void test_error_struct_free(TestErrorStruct *)
Free a TestErrorStruct created with test_error_struct_new.

◆ test_list_handler()

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.

If the list is empty or nothing matches, passes control on to test_checked_handler, giving the opportunity for an additional check that's not in the list (set user_data to NULL if you want test_checked_handler to immediately print the error).

Definition at line 194 of file unittest-support.c.

196 {
197  return do_test_list_handler (log_domain, log_level, msg, user_data, TRUE);
198 }

◆ test_list_substring_handler()

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.

Rather than checking for an exact match, this function checks using a substring match. If the list is empty or nothing matches, passes control on to test_checked_substring_handler, giving the opportunity for an additional check that's not in the list (set user_data to NULL if you want test_checked_handler to immediately print the error).

Definition at line 148 of file unittest-support.c.

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 }
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...
Struct to pass as user_data for the handlers.

◆ test_log_handler()

gboolean test_log_handler ( const char *  log_domain,
GLogLevelFlags  log_level,
const gchar *  msg,
gpointer  user_data 
)

Just print the log message.

Since GLib has a habit of eating its log messages, it's sometimes useful to call g_test_log_set_fatal_handler() with this to make sure that g_return_if_fail() error messages make it to the surface.

Definition at line 269 of file unittest-support.c.

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 }

◆ test_log_set_fatal_handler()

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 the test program doesn't abort for fatal log messages.

If a test function has more than one fatal message, be sure to use the test_list_handler!

Don't pass a NULL TestErrorStruct! It's needed to set the parameters for g_log_set_handler. Use a TestErrorStruct created with test_error_struct_new() or you'll have errors with freeing it in teardown.

Parameters
handler_listA GSList of LogHandlers
errorA TestErrorStruct with the necessary data
handlerThe Handler to set the data with
Returns
: The new GSList pointer.

Definition at line 73 of file unittest-support.c.

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 }
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.
Holds a handler instance with its TestErrorStruct, handler id, and whether it&#39;s a list handler...

◆ test_log_set_handler()

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.

Don't pass a NULL TestErrorStruct! It's needed to set the parameters for g_log_set_handler. Use a TestErrorStruct created with test_error_struct_new() or you'll have errors with freeing it in teardown.

Parameters
handler_listA GSList of LogHandlers
errorA TestErrorStruct with the necessary data
handlerThe Handler to set the data with
Returns
: The new GSList pointer.

Definition at line 63 of file unittest-support.c.

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 }
Holds a handler instance with its TestErrorStruct, handler id, and whether it&#39;s a list handler...

◆ test_null_handler()

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.

Use this only as a last resort.

Definition at line 97 of file unittest-support.c.

99 {
100  //Silent, remember?
101 
102  return FALSE;
103 }

◆ test_reset_data()

gpointer test_reset_data ( void  )

Destructively retrieves the test data pointer.

Call from your mock to ensure that it received the expected data.

Definition at line 300 of file unittest-support.c.

301 {
302  const gpointer data = tdata.data;
303  tdata.data = NULL;
304  return data;
305 }

◆ test_signal_new()

TestSignal test_signal_new ( QofInstance entity,
QofEventId  eventType,
gpointer  event_data 
)

Create a test signal.

Parameters
entityThe QofInstance emitting the signal
eventTypeThe type of the signal
event_dataAny data required by the signal or NULL if none is.
Returns
A newly created TestSignal. Use test_signal_free to release it.

Definition at line 339 of file unittest-support.c.

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 }
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
Definition: qofevent.cpp:73
gpointer TestSignal
TestSignal is an opaque struct used to mock handling signals emitted by functions-under-test.