Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if a gobject was correctly freed

I'm using glib's Testing framework for unit testing. My library also uses gobject, in my test unities I want to check if after the last _unref of each object the object was correctly freed. When g_test_trap_fork was available I used it after each _unref, calling _unref for a second time and then checking for g_test_trap_assert_failed ().

However, now that g_test_trap_fork is becoming deprecated I'm moving towards g_test_trap_subprocess. The problem is that now I would have to write a separated test case for each _unref to be checked and since each case could contain several objects that would imply on the repetition of every test case adding a second _unref for each one already present.

For example, I tried to fix like this:

NcmVector *v = test->v;
GVariant *var = ncm_vector_get_variant (v);

g_assert (!g_variant_is_floating (var));
g_assert (g_variant_is_container (var));
g_assert_cmpuint (ncm_vector_len (v), ==, g_variant_n_children (var));

{
  NcmVector *nv = ncm_vector_new_variant (var);
  gint i;

  g_assert_cmpuint (ncm_vector_len (v), ==, ncm_vector_len (nv));
  for (i = 0; i < ncm_vector_len (v); i++)
  {
    ncm_assert_cmpdouble (ncm_vector_get (v, i), ==, ncm_vector_get (nv, i));
  }

  ncm_vector_free (nv);
  NCM_TEST_FAIL (ncm_vector_free (nv));
}

g_variant_unref (var);
NCM_TEST_FAIL (g_variant_unref (var); fprintf (stderr, "fail (%s)", g_variant_get_type_string (var)));

Where the macro NCM_TEST_FAIL is given by:

#define NCM_TEST_FAIL(cmd) \
G_STMT_START { \
  if (g_test_subprocess ()) \
  { \
    cmd; \
    exit (0); \
  } \
  else \
  { \
    g_test_trap_subprocess (NULL, 0, 0); \
    g_test_trap_assert_failed (); \
  } \
} G_STMT_END

The problem with this solution is that it can be only used once in each test case. If used for a second time, as in the example above, it would only test the first appearance of g_test_subprocess ().

I thought about checking inside of the gobject structure for the reference count just before the last _unref to check if it is == 1. But that would involve accessing a private part of that structure which I would avoid if possible.

Any ideas of how to check for erroneous code several times inside the same test case?

like image 723
monojohnny Avatar asked Dec 14 '22 21:12

monojohnny


2 Answers

if you need to check for a GObject to be disposed correctly, you can use g_object_add_weak_pointer(), for instance:

FooObject *o = g_object_new (foo_object_get_type (), NULL);

g_assert_nonnull (o);

// the contents of the pointer are reset to NULL when the last reference to
// the GObject instance goes away; by passing a pointer to the same instance
// we can check it for NULL later
g_object_add_weak_pointer (G_OBJECT (o), (gpointer *) &o);

// ...test FooObject...

// drop the last reference
g_object_unref (o);

// at this point, the object should be NULL if nothing is holding
// an additional reference.
g_assert_null (o);

in the example above, though, you're using GVariant, which is not a GObject, and thus it does not have weak references.

GLib does not have a common reference counted type for binary compatibility reasons; all the common code for a reference counted type is in GObject.

like image 129
ebassi Avatar answered Dec 17 '22 09:12

ebassi


You can check the number of instance of a type with g_type_instance_count (export GOBJECT_DEBUG=instance-count)

MyObject *obj = g_object_new (my_object_get_type (), NULL);

g_type_get_instance_count(my_object_get_type ());// return 1
g_object_unref(obj);
g_type_get_instance_count(my_object_get_type ());// return 0

but you can check memory leak with Valgrind.

like image 45
user7348373 Avatar answered Dec 17 '22 11:12

user7348373