【问题标题】:Check if a gobject was correctly freed检查 gobject 是否被正确释放
【发布时间】:2014-06-27 13:38:24
【问题描述】:

我正在使用 glib 的测试框架进行单元测试。我的库也使用 gobject,在我的测试单元中,我想检查在每个对象的最后一个 _unref 之后对象是否被正确释放。当 g_test_trap_fork 可用时,我在每个 _unref 之后使用它,第二次调用 _unref 然后检查 g_test_trap_assert_failed ()。

但是,现在 g_test_trap_fork 已被弃用,我正在转向 g_test_trap_subprocess。问题是现在我必须为每个要检查的 _unref 编写一个单独的测试用例,因为每个案例可能包含几个对象,这意味着每个测试用例的重复,为每个已经存在的每个测试用例添加第二个 _unref。

例如,我尝试这样修复:

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)));

宏 NCM_TEST_FAIL 由以下给出:

#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

这个解决方案的问题是它在每个测试用例中只能使用一次。如果第二次使用,如上例,只会测试g_test_subprocess()的第一次出现。

我曾考虑在最后一个 _unref 之前检查 gobject 结构内部的引用计数,以检查它是否 == 1。但这将涉及访问该结构的私有部分,如果可能的话我会避免这样做。

关于如何在同一个测试用例中多次检查错误代码有什么想法吗?

【问题讨论】:

  • GVariant 不是 GObject。
  • @nemequ 是的,我知道,它不是 GObject,这是寻找一种通用方法来检查引用计数的“对象”是否被正确释放的另一个原因。

标签: unit-testing glib gobject


【解决方案1】:

如果您需要检查GObject 是否被正确处理,您可以使用g_object_add_weak_pointer(),例如:

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);

不过,在上面的示例中,您使用的是GVariant,它不是GObject,因此它没有弱引用。

出于二进制兼容性的原因,GLib 没有通用的引用计数类型;引用计数类型的所有通用代码都在 GObject 中。

【讨论】:

  • 关于如何解决 GArrays、GHashTables、GVariants 等问题的任何想法?
  • 你不能:它们可能是 refcounted 类型(最近,对于 GArray 和 GHashTable),但它们没有弱引用,因此你无法知道最后一个引用是否已被删除。
  • 替代g_assert_nonnull,可以终止程序,可以使用g_print("%p\n", o)。与NULL 比较可能会出现段错误。
【解决方案2】:

您可以使用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

但您可以使用 Valgrind 检查内存泄漏。

【讨论】:

    猜你喜欢
    • 2011-11-16
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多