我正在使用 glib 的测试框架进行单元测试。我的库还使用 gobject,在我的测试单元中,我想检查每个对象的最后一个 _unref 之后该对象是否已正确释放。当 g_test_trap_fork 可用时,我在每次 _unref 之后使用它,第二次调用 _unref,然后检查 g_test_trap_assert_failed ()。
但是,现在 g_test_trap_fork 已被弃用,我正在转向 g_test_trap_subprocess。问题是,现在我必须为要检查的每个 _unref 编写一个单独的测试用例,并且因为每个案例都可能包含多个对象,这意味着每个测试用例的重复都会为每个已存在的_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。但这将涉及访问该结构的私有部分,如果可能的话我会避免这样做。
关于如何在同一测试用例中多次检查错误代码有什么想法吗?
如果您需要检查
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
中。
您可以使用 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 检查内存泄漏。
对于那些将对象存储在小部件上的人,您也可以在释放对象时简单地使用
g_object_steal_data
而不是 g_object_get_data
,这样您就可以通过 g_object_get_data
检查它的存在。
g_object_set_data (G_OBJECT (widget), "key", object);
g_object_get_data (G_OBJECT (widget), "key")); // Expect non-NULL
g_object_unref (g_object_steal_data (G_OBJECT (widget), "key"));
g_object_get_data (G_OBJECT (widget), "key")); // Expect NULL
此方法仅适用于单引用对象。