diff --git a/glib/tests/meson.build b/glib/tests/meson.build
index e0619c475..cf05bc74f 100644
--- a/glib/tests/meson.build
+++ b/glib/tests/meson.build
@@ -47,6 +47,8 @@ glib_tests = [
'queue',
'rand',
'rec-mutex',
+ 'refcount',
+ 'refcount-macro',
'regex',
'rwlock',
'scannerapi',
@@ -108,14 +110,23 @@ slow_tests = [
foreach test_name : glib_tests
deps = [libm, thread_dep, libglib_dep]
+ source = test_name + '.c'
+ c_args = test_cargs + ['-DPCRE_STATIC']
if test_name == 'regex'
deps += [pcre]
endif
if test_name == 'gdatetime'
deps += [libintl]
endif
- exe = executable(test_name, '@0@.c'.format(test_name),
- c_args : ['-DPCRE_STATIC'] + test_cargs,
+ # We build the refcount test twice: one to test the function-based API,
+ # and the other to test the macro-based API that is used when disabling
+ # checks
+ if test_name == 'refcount-macro'
+ source = 'refcount.c'
+ c_args += ['-DG_DISABLE_CHECKS']
+ endif
+ exe = executable(test_name, source,
+ c_args : c_args,
dependencies : deps,
install : false,
)
diff --git a/glib/tests/refcount.c b/glib/tests/refcount.c
new file mode 100644
index 000000000..dfccc92c7
--- /dev/null
+++ b/glib/tests/refcount.c
@@ -0,0 +1,221 @@
+/* refcount.c: Tests for reference counting types
+ *
+ * Copyright 2018 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#include
+#include
+
+/* test_grefcount: test the behavior of the grefcount API */
+static void
+test_grefcount (void)
+{
+ grefcount a, b;
+
+ /* init(a): 1 */
+ g_ref_count_init (&a);
+ if (g_test_verbose ())
+ g_test_message ("init(a) := %d\n", (int) a);
+ g_assert_true (g_ref_count_compare (&a, 1));
+
+ /* inc(a): 2 */
+ g_ref_count_inc (&a);
+ if (g_test_verbose ())
+ g_test_message ("inc(a) := %d\n", (int) a);
+ g_assert_false (g_ref_count_compare (&a, 1));
+ g_assert_false (g_ref_count_compare (&a, G_MAXINT));
+
+ /* b = a = 2 */
+ b = a;
+ if (g_test_verbose ())
+ g_test_message ("a := %d, b := %d\n", (int) a, (int) b);
+
+ /* inc(a): 3 */
+ g_ref_count_inc (&a);
+ if (g_test_verbose ())
+ g_test_message ("inc(a) := %d\n", (int) a);
+
+ /* dec(b) = 1 */
+ if (g_test_verbose ())
+ g_test_message ("dec(b) := %d + 1\n", (int) b);
+ g_assert_false (g_ref_count_dec (&b));
+
+ /* dec(a) = 2 */
+ if (g_test_verbose ())
+ g_test_message ("dec(a) := %d + 1\n", (int) a);
+ g_assert_false (g_ref_count_dec (&a));
+
+ /* dec(b) = 0 */
+ if (g_test_verbose ())
+ g_test_message ("dec(b) := %d + 1\n", (int) b);
+ g_assert_true (g_ref_count_dec (&b));
+
+ /* dec(a) = 1 */
+ if (g_test_verbose ())
+ g_test_message ("dec(a) := %d + 1\n", (int) a);
+ g_assert_false (g_ref_count_dec (&a));
+
+ /* dec(a) = 0 */
+ if (g_test_verbose ())
+ g_test_message ("dec(a) := %d + 1\n", (int) a);
+ g_assert_true (g_ref_count_dec (&a));
+}
+
+/* test_grefcount_saturation: Saturating a grefcount counter
+ * does not cause an overflow; additionally, if we're building
+ * with checks enabled, it'll cause a warning
+ */
+static void
+test_grefcount_saturation (void)
+{
+ if (g_test_subprocess ())
+ {
+ grefcount a;
+
+ /* We're breaking abstraction here for convenience */
+ a = G_MININT + 1;
+
+ g_ref_count_inc (&a);
+ g_assert_true (a == G_MININT);
+
+ g_ref_count_inc (&a);
+ g_assert_true (a == G_MININT);
+
+ exit (0);
+ }
+
+ g_test_trap_subprocess (NULL, 0, 0);
+
+#ifndef G_DISABLE_CHECKS
+ /* Ensure that we got a warning when building with checks; the
+ * test will fail because of the critical warning being caught
+ * by GTest
+ */
+ g_test_trap_assert_failed ();
+ g_test_trap_assert_stderr ("*saturation*");
+#else
+ /* With checks disabled we don't get any warning */
+ g_test_trap_assert_passed ();
+#endif
+}
+
+/* test_gatomicrefcount: test the behavior of the gatomicrefcount API */
+static void
+test_gatomicrefcount (void)
+{
+ gatomicrefcount a, b;
+
+ /* init(a): 1 */
+ g_atomic_ref_count_init (&a);
+ if (g_test_verbose ())
+ g_test_message ("init(a) := %d\n", (int) a);
+ g_assert_true (g_atomic_ref_count_compare (&a, 1));
+
+ /* inc(a): 2 */
+ g_atomic_ref_count_inc (&a);
+ if (g_test_verbose ())
+ g_test_message ("inc(a) := %d\n", (int) a);
+ g_assert_false (g_atomic_ref_count_compare (&a, 1));
+ g_assert_false (g_atomic_ref_count_compare (&a, G_MAXINT));
+
+ /* b = a = 2 */
+ b = a;
+ if (g_test_verbose ())
+ g_test_message ("a := %d, b := %d\n", (int) a, (int) b);
+
+ /* inc(a): 3 */
+ g_atomic_ref_count_inc (&a);
+ if (g_test_verbose ())
+ g_test_message ("inc(a) := %d\n", (int) a);
+
+ /* dec(b) = 1 */
+ if (g_test_verbose ())
+ g_test_message ("dec(b) := %d + 1\n", (int) b);
+ g_assert_false (g_atomic_ref_count_dec (&b));
+
+ /* dec(a) = 2 */
+ if (g_test_verbose ())
+ g_test_message ("dec(a) := %d + 1\n", (int) a);
+ g_assert_false (g_atomic_ref_count_dec (&a));
+
+ /* dec(b) = 0 */
+ if (g_test_verbose ())
+ g_test_message ("dec(b) := %d + 1\n", (int) b);
+ g_assert_true (g_atomic_ref_count_dec (&b));
+
+ /* dec(a) = 1 */
+ if (g_test_verbose ())
+ g_test_message ("dec(a) := %d + 1\n", (int) a);
+ g_assert_false (g_atomic_ref_count_dec (&a));
+
+ /* dec(a) = 0 */
+ if (g_test_verbose ())
+ g_test_message ("dec(a) := %d + 1\n", (int) a);
+ g_assert_true (g_atomic_ref_count_dec (&a));
+}
+
+/* test_grefcount_saturation: Saturating a gatomicrefcount counter
+ * does not cause an overflow; additionally, if we're building
+ * with checks enabled, it'll cause a warning
+ */
+static void
+test_gatomicrefcount_saturation (void)
+{
+ if (g_test_subprocess ())
+ {
+ gatomicrefcount a;
+
+ /* We're breaking abstraction here for convenience */
+ a = G_MAXINT - 1;
+
+ g_atomic_ref_count_inc (&a);
+ g_assert_true (a == G_MAXINT);
+
+ g_atomic_ref_count_inc (&a);
+ g_assert_true (a == G_MAXINT);
+
+ exit (0);
+ }
+
+ g_test_trap_subprocess (NULL, 0, 0);
+
+#ifndef G_DISABLE_CHECKS
+ /* Ensure that we got a warning when building with checks; the
+ * test will fail because of the critical warning being caught
+ * by GTest
+ */
+ g_test_trap_assert_failed ();
+ g_test_trap_assert_stderr ("*saturation*");
+#else
+ /* With checks disabled we don't get any warning */
+ g_test_trap_assert_passed ();
+#endif
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/refcount/grefcount", test_grefcount);
+ g_test_add_func ("/refcount/grefcount/saturation", test_grefcount_saturation);
+
+ g_test_add_func ("/refcount/gatomicrefcount", test_gatomicrefcount);
+ g_test_add_func ("/refcount/gatomicrefcount/saturation", test_gatomicrefcount_saturation);
+
+ return g_test_run ();
+}