[test/object] Add test for object lifecycle stuff
authorBehdad Esfahbod <behdad@behdad.org>
Mon, 2 May 2011 23:52:47 +0000 (19:52 -0400)
committerBehdad Esfahbod <behdad@behdad.org>
Mon, 2 May 2011 23:52:47 +0000 (19:52 -0400)
Revealed many bugs in the (untested and known buggy) user_data
support.

test/Makefile.am
test/test-object.c [new file with mode: 0644]

index e48708b..56d6118 100644 (file)
@@ -12,6 +12,7 @@ noinst_PROGRAMS = $(TEST_PROGS)
 TEST_PROGS += \
        test-buffer \
        test-common \
+       test-object \
        test-unicode \
        $(NULL)
 
@@ -31,6 +32,9 @@ endif
 if HAVE_FREETYPE
 test_c_CPPFLAGS += $(FREETYPE_CFLAGS)
 test_cplusplus_CPPFLAGS += $(FREETYPE_CFLAGS)
+# TODO replace freetype with other stuff in the following test
+test_object_CPPFLAGS = $(AM_CPPFLAGS) $(FREETYPE_CFLAGS)
+test_object_LIBS = $(LDADD) $(FREETYPE_LIBS)
 endif
 
 
diff --git a/test/test-object.c b/test/test-object.c
new file mode 100644 (file)
index 0000000..0fbabd8
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * Copyright © 2011  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-test.h"
+
+/* Unit tests for hb-object-private.h */
+
+
+#ifdef HAVE_FREETYPE
+#include <hb-ft.h>
+#endif
+
+
+static void *
+create_blob (void)
+{
+  static char data[] = "test data";
+  return hb_blob_create (data, sizeof (data), HB_MEMORY_MODE_READONLY, NULL, NULL);
+}
+static void *
+create_blob_inert (void)
+{
+  return hb_blob_get_empty ();
+}
+
+static void *
+create_buffer (void)
+{
+  return hb_buffer_create (0);
+}
+static void *
+create_buffer_inert (void)
+{
+  return hb_buffer_create (-1);
+}
+
+static void *
+create_face (void)
+{
+  hb_blob_t *blob = (hb_blob_t *) create_blob ();
+  hb_face_t *face = hb_face_create_for_data (blob, 0);
+  hb_blob_destroy (blob);
+  return face;
+}
+static void *
+create_face_inert (void)
+{
+  return hb_face_create_for_data ((hb_blob_t *) create_blob_inert (), 0);
+}
+
+static void *
+create_font (void)
+{
+  return hb_font_create ();
+}
+static void *
+create_font_inert (void)
+{
+  return NULL;
+}
+
+static void *
+create_font_funcs (void)
+{
+  return hb_font_funcs_create ();
+}
+static void *
+create_font_funcs_inert (void)
+{
+#ifdef HAVE_FREETYPE
+  return hb_ft_get_font_funcs ();
+#else
+  return NULL;
+#endif
+}
+
+static void *
+create_unicode_funcs (void)
+{
+  return hb_unicode_funcs_create (NULL);
+}
+static void *
+create_unicode_funcs_inert (void)
+{
+  return hb_unicode_funcs_get_default ();
+}
+
+
+
+typedef void     *(*create_func_t)         (void);
+typedef void     *(*reference_func_t)      (void *obj);
+typedef void      (*destroy_func_t)        (void *obj);
+typedef hb_bool_t (*set_user_data_func_t)  (void *obj, hb_user_data_key_t *key, void *data, hb_destroy_func_t destroy);
+typedef void *    (*get_user_data_func_t)  (void *obj, hb_user_data_key_t *key);
+typedef void      (*make_immutable_func_t) (void *obj);
+typedef hb_bool_t (*is_immutable_func_t)   (void *obj);
+
+typedef struct {
+  create_func_t          create;
+  create_func_t          create_inert;
+  reference_func_t       reference;
+  destroy_func_t         destroy;
+  set_user_data_func_t   set_user_data;
+  get_user_data_func_t   get_user_data;
+  make_immutable_func_t  make_immutable;
+  is_immutable_func_t    is_immutable;
+  const char            *name;
+} object_t;
+
+#define OBJECT_WITHOUT_IMMUTABILITY(name) \
+  { \
+    (create_func_t)         create_##name, \
+    (create_func_t)         create_##name##_inert, \
+    (reference_func_t)      hb_##name##_reference, \
+    (destroy_func_t)        hb_##name##_destroy, \
+    (set_user_data_func_t)  hb_##name##_set_user_data, \
+    (get_user_data_func_t)  hb_##name##_get_user_data, \
+    (make_immutable_func_t) NULL, \
+    (is_immutable_func_t)   NULL, \
+    #name, \
+  }
+#define OBJECT_WITH_IMMUTABILITY(name) \
+  { \
+    (create_func_t)         create_##name, \
+    (create_func_t)         create_##name##_inert, \
+    (reference_func_t)      hb_##name##_reference, \
+    (destroy_func_t)        hb_##name##_destroy, \
+    (set_user_data_func_t)  hb_##name##_set_user_data, \
+    (get_user_data_func_t)  hb_##name##_get_user_data, \
+    (make_immutable_func_t) hb_##name##_make_immutable, \
+    (is_immutable_func_t)   hb_##name##_is_immutable, \
+    #name, \
+  }
+static const object_t objects[] =
+{
+  OBJECT_WITHOUT_IMMUTABILITY (blob),
+  OBJECT_WITHOUT_IMMUTABILITY (buffer),
+  OBJECT_WITHOUT_IMMUTABILITY (face),
+  OBJECT_WITHOUT_IMMUTABILITY (font),
+  OBJECT_WITH_IMMUTABILITY (font_funcs),
+  OBJECT_WITH_IMMUTABILITY (unicode_funcs)
+};
+#undef OBJECT
+
+
+#define MAGIC0 0x12345678
+#define MAGIC1 0x76543210
+
+typedef struct {
+  int value;
+  gboolean freed;
+} data_t;
+
+static int global_data;
+
+static void global_free_up (void *p G_GNUC_UNUSED)
+{
+  global_data++;
+}
+
+static void free_up0 (void *p)
+{
+  data_t *data = (data_t *) p;
+
+  g_assert_cmphex (data->value, ==, MAGIC0);
+  g_assert (!data->freed);
+  data->freed = TRUE;
+}
+
+static void free_up1 (void *p)
+{
+  data_t *data = (data_t *) p;
+
+  g_assert_cmphex (data->value, ==, MAGIC1);
+  g_assert (!data->freed);
+  data->freed = TRUE;
+}
+
+static void
+test_object (void)
+{
+  unsigned int i;
+
+  for (i = 0; i < G_N_ELEMENTS (objects); i++) {
+    const object_t *o = &objects[i];
+    void *obj;
+    hb_user_data_key_t key[2];
+
+    {
+      unsigned int i;
+      data_t data[2] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}};
+
+      g_test_message ("Testing object %s", o->name);
+
+      g_test_message ("->create()");
+      obj = o->create ();
+      g_assert (obj);
+
+      g_assert (obj == o->reference (obj));
+      o->destroy (obj);
+
+      if (o->is_immutable)
+       g_assert (!o->is_immutable (obj));
+
+      g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0));
+      g_assert (o->get_user_data (obj, &key[0]) == &data[0]);
+
+      if (o->is_immutable) {
+       o->make_immutable (obj);
+       g_assert (o->is_immutable (obj));
+      }
+
+      /* Should still work even if object is made immutable */
+      g_assert (o->set_user_data (obj, &key[1], &data[1], free_up1));
+      g_assert (o->get_user_data (obj, &key[1]) == &data[1]);
+
+      g_assert (!o->set_user_data (obj, NULL, &data[0], free_up0));
+      g_assert (o->get_user_data (obj, &key[0]) == &data[0]);
+      g_assert (o->set_user_data (obj, &key[0], &data[1], NULL));
+      g_assert (data[0].freed);
+      g_assert (o->get_user_data (obj, &key[0]) == &data[1]);
+      g_assert (!data[1].freed);
+
+      data[0].freed = FALSE;
+      g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0));
+      g_assert (!data[0].freed);
+      g_assert (o->set_user_data (obj, &key[0], NULL, NULL));
+      g_assert (data[0].freed);
+
+      data[0].freed = FALSE;
+      global_data = 0;
+      g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0));
+      g_assert_cmpuint (global_data, ==, 0);
+      g_assert (o->set_user_data (obj, &key[0], NULL, global_free_up));
+      g_assert_cmpuint (global_data, ==, 0);
+      g_assert (o->set_user_data (obj, &key[0], NULL, NULL));
+      g_assert_cmpuint (global_data, ==, 1);
+
+      global_data = 0;
+      for (i = 2; i < 1000; i++)
+       g_assert (o->set_user_data (obj, &key[i], &data[i], global_free_up));
+      for (i = 2; i < 1000; i++)
+       g_assert (o->get_user_data (obj, &key[i]) == &data[i]);
+      for (i = 100; i < 1000; i++)
+       g_assert (o->set_user_data (obj, &key[i], NULL, NULL));
+      g_assert_cmpuint (global_data, ==, 900);
+
+
+      g_assert (!data[1].freed);
+      o->destroy (obj);
+      g_assert (data[0].freed);
+      g_assert (data[1].freed);
+      g_assert_cmpuint (global_data, ==, 1000-2);
+    }
+
+    {
+      data_t data[2] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}};
+
+      g_test_message ("->create_inert()");
+      obj = o->create_inert ();
+      if (!obj)
+       continue;
+
+      g_assert (obj == o->reference (obj));
+      o->destroy (obj);
+
+      if (o->is_immutable)
+       g_assert (o->is_immutable (obj));
+
+      g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0));
+      g_assert (!o->get_user_data (obj, &key[0]));
+
+      o->destroy (obj);
+      o->destroy (obj);
+      o->destroy (obj);
+      o->destroy (obj);
+      o->destroy (obj);
+
+      g_assert (!data[0].freed);
+    }
+  }
+}
+
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_object);
+
+  return hb_test_run ();
+}