1/*
2 * Copyright © 2011  Google, Inc.
3 *
4 *  This is part of HarfBuzz, a text shaping library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 *
24 * Google Author(s): Behdad Esfahbod
25 */
26
27#include "hb-test.h"
28
29/* Unit tests for hb-object-private.h */
30
31
32static void *
33create_blob (void)
34{
35  static char data[] = "test data";
36  return hb_blob_create (data, sizeof (data), HB_MEMORY_MODE_READONLY, NULL, NULL);
37}
38static void *
39create_blob_inert (void)
40{
41  return hb_blob_create (NULL, 0, HB_MEMORY_MODE_DUPLICATE, NULL, NULL);
42}
43
44static void *
45create_buffer (void)
46{
47  return hb_buffer_create ();
48}
49static void *
50create_buffer_inert (void)
51{
52  return NULL;
53}
54
55static void *
56create_set (void)
57{
58  return hb_set_create ();
59}
60static void *
61create_set_inert (void)
62{
63  return NULL;
64}
65
66static void *
67create_face (void)
68{
69  hb_blob_t *blob = (hb_blob_t *) create_blob ();
70  hb_face_t *face = hb_face_create (blob, 0);
71  hb_blob_destroy (blob);
72  return face;
73}
74static void *
75create_face_inert (void)
76{
77  return hb_face_create (hb_blob_get_empty (), 0);
78}
79
80static void *
81create_font (void)
82{
83  hb_face_t *face = (hb_face_t *) create_face ();
84  hb_font_t *font = hb_font_create (face);
85  hb_face_destroy (face);
86  return font;
87}
88static void *
89create_font_inert (void)
90{
91  return hb_font_create (hb_face_get_empty ());
92}
93
94static void *
95create_font_funcs (void)
96{
97  return hb_font_funcs_create ();
98}
99static void *
100create_font_funcs_inert (void)
101{
102  return NULL;
103}
104
105static void *
106create_unicode_funcs (void)
107{
108  return hb_unicode_funcs_create (NULL);
109}
110static void *
111create_unicode_funcs_inert (void)
112{
113  return hb_unicode_funcs_get_default ();
114}
115
116
117
118typedef void     *(*create_func_t)         (void);
119typedef void     *(*reference_func_t)      (void *obj);
120typedef void      (*destroy_func_t)        (void *obj);
121typedef hb_bool_t (*set_user_data_func_t)  (void *obj, hb_user_data_key_t *key, void *data, hb_destroy_func_t destroy, hb_bool_t replace);
122typedef void *    (*get_user_data_func_t)  (void *obj, hb_user_data_key_t *key);
123typedef void      (*make_immutable_func_t) (void *obj);
124typedef hb_bool_t (*is_immutable_func_t)   (void *obj);
125
126typedef struct {
127  create_func_t          create;
128  create_func_t          create_inert;
129  create_func_t          get_empty;
130  reference_func_t       reference;
131  destroy_func_t         destroy;
132  set_user_data_func_t   set_user_data;
133  get_user_data_func_t   get_user_data;
134  make_immutable_func_t  make_immutable;
135  is_immutable_func_t    is_immutable;
136  const char            *name;
137} object_t;
138
139#define OBJECT_WITHOUT_IMMUTABILITY(name) \
140  { \
141    (create_func_t)         create_##name, \
142    (create_func_t)         create_##name##_inert, \
143    (create_func_t)         hb_##name##_get_empty, \
144    (reference_func_t)      hb_##name##_reference, \
145    (destroy_func_t)        hb_##name##_destroy, \
146    (set_user_data_func_t)  hb_##name##_set_user_data, \
147    (get_user_data_func_t)  hb_##name##_get_user_data, \
148    (make_immutable_func_t) NULL, \
149    (is_immutable_func_t)   NULL, \
150    #name, \
151  }
152#define OBJECT_WITH_IMMUTABILITY(name) \
153  { \
154    (create_func_t)         create_##name, \
155    (create_func_t)         create_##name##_inert, \
156    (create_func_t)         hb_##name##_get_empty, \
157    (reference_func_t)      hb_##name##_reference, \
158    (destroy_func_t)        hb_##name##_destroy, \
159    (set_user_data_func_t)  hb_##name##_set_user_data, \
160    (get_user_data_func_t)  hb_##name##_get_user_data, \
161    (make_immutable_func_t) hb_##name##_make_immutable, \
162    (is_immutable_func_t)   hb_##name##_is_immutable, \
163    #name, \
164  }
165static const object_t objects[] =
166{
167  OBJECT_WITHOUT_IMMUTABILITY (buffer),
168  OBJECT_WITHOUT_IMMUTABILITY (set),
169  OBJECT_WITH_IMMUTABILITY (blob),
170  OBJECT_WITH_IMMUTABILITY (face),
171  OBJECT_WITH_IMMUTABILITY (font),
172  OBJECT_WITH_IMMUTABILITY (font_funcs),
173  OBJECT_WITH_IMMUTABILITY (unicode_funcs)
174};
175#undef OBJECT
176
177
178#define MAGIC0 0x12345678
179#define MAGIC1 0x76543210
180
181typedef struct {
182  int value;
183  gboolean freed;
184} data_t;
185
186static int global_data;
187
188static void global_free_up (void *p G_GNUC_UNUSED)
189{
190  global_data++;
191}
192
193static void free_up0 (void *p)
194{
195  data_t *data = (data_t *) p;
196
197  g_assert_cmphex (data->value, ==, MAGIC0);
198  g_assert (!data->freed);
199  data->freed = TRUE;
200}
201
202static void free_up1 (void *p)
203{
204  data_t *data = (data_t *) p;
205
206  g_assert_cmphex (data->value, ==, MAGIC1);
207  g_assert (!data->freed);
208  data->freed = TRUE;
209}
210
211
212typedef struct {
213  const object_t *klass;
214  void *object;
215  hb_user_data_key_t key;
216} deadlock_test_t;
217
218static void free_deadlock_test (void *p)
219{
220  deadlock_test_t *t = (deadlock_test_t *) p;
221
222  g_assert (NULL == t->klass->get_user_data (t->object, &t->key));
223}
224
225
226static void
227test_object (void)
228{
229  unsigned int i;
230
231  for (i = 0; i < G_N_ELEMENTS (objects); i++) {
232    const object_t *o = &objects[i];
233    void *obj;
234    hb_user_data_key_t key[1001];
235
236    {
237      unsigned int j;
238      data_t data[2] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}};
239      deadlock_test_t deadlock_test;
240
241      g_test_message ("Testing object %s", o->name);
242
243      g_test_message ("->create()");
244      obj = o->create ();
245      g_assert (obj);
246
247      g_assert (obj == o->reference (obj));
248      o->destroy (obj);
249
250      if (o->is_immutable)
251	g_assert (!o->is_immutable (obj));
252
253      g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE));
254      g_assert (o->get_user_data (obj, &key[0]) == &data[0]);
255
256      if (o->is_immutable) {
257	o->make_immutable (obj);
258	g_assert (o->is_immutable (obj));
259      }
260
261      /* Should still work even if object is made immutable */
262      g_assert (o->set_user_data (obj, &key[1], &data[1], free_up1, TRUE));
263      g_assert (o->get_user_data (obj, &key[1]) == &data[1]);
264
265      g_assert (!o->set_user_data (obj, NULL, &data[0], free_up0, TRUE));
266      g_assert (o->get_user_data (obj, &key[0]) == &data[0]);
267      g_assert (o->set_user_data (obj, &key[0], &data[1], NULL, TRUE));
268      g_assert (data[0].freed);
269      g_assert (o->get_user_data (obj, &key[0]) == &data[1]);
270      g_assert (!data[1].freed);
271
272      data[0].freed = FALSE;
273      g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE));
274      g_assert (!data[0].freed);
275      g_assert (o->set_user_data (obj, &key[0], NULL, NULL, TRUE));
276      g_assert (data[0].freed);
277
278      data[0].freed = FALSE;
279      global_data = 0;
280      g_assert (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE));
281      g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0, FALSE));
282      g_assert_cmpuint (global_data, ==, 0);
283      g_assert (o->set_user_data (obj, &key[0], NULL, global_free_up, TRUE));
284      g_assert_cmpuint (global_data, ==, 0);
285      g_assert (o->set_user_data (obj, &key[0], NULL, NULL, TRUE));
286      g_assert_cmpuint (global_data, ==, 1);
287
288      global_data = 0;
289      for (j = 2; j < 1000; j++)
290	g_assert (o->set_user_data (obj, &key[j], &data[j], global_free_up, TRUE));
291      for (j = 2; j < 1000; j++)
292	g_assert (o->get_user_data (obj, &key[j]) == &data[j]);
293      for (j = 100; j < 1000; j++)
294	g_assert (o->set_user_data (obj, &key[j], NULL, NULL, TRUE));
295      for (j = 2; j < 100; j++)
296	g_assert (o->get_user_data (obj, &key[j]) == &data[j]);
297      for (j = 100; j < 1000; j++)
298	g_assert (!o->get_user_data (obj, &key[j]));
299      g_assert_cmpuint (global_data, ==, 900);
300
301      /* Test set_user_data where the destroy() func calls user_data functions.
302       * Make sure it doesn't deadlock or corrupt memory. */
303      deadlock_test.klass = o;
304      deadlock_test.object = obj;
305      g_assert (o->set_user_data (obj, &deadlock_test.key, &deadlock_test, free_deadlock_test, TRUE));
306      g_assert (o->set_user_data (obj, &deadlock_test.key, NULL, NULL, TRUE));
307
308      g_assert (!data[1].freed);
309      o->destroy (obj);
310      g_assert (data[0].freed);
311      g_assert (data[1].freed);
312      g_assert_cmpuint (global_data, ==, 1000-2);
313    }
314
315    {
316      data_t data[2] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}};
317
318      g_test_message ("->get_empty()");
319      obj = o->get_empty ();
320      g_assert (obj);
321
322      g_assert (obj == o->reference (obj));
323      o->destroy (obj);
324
325      if (o->is_immutable)
326	g_assert (o->is_immutable (obj));
327
328      g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE));
329      g_assert (!o->get_user_data (obj, &key[0]));
330
331      o->destroy (obj);
332      o->destroy (obj);
333      o->destroy (obj);
334      o->destroy (obj);
335      o->destroy (obj);
336
337      g_assert (!data[0].freed);
338    }
339
340    {
341      data_t data[2] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}};
342
343      g_test_message ("->create_inert()");
344      obj = o->create_inert ();
345      if (!obj)
346	continue;
347      if (obj == o->get_empty ())
348        continue; /* Tested already */
349
350      g_assert (obj == o->reference (obj));
351      o->destroy (obj);
352
353      if (o->is_immutable)
354	g_assert (o->is_immutable (obj));
355
356      g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE));
357      g_assert (!o->get_user_data (obj, &key[0]));
358
359      o->destroy (obj);
360      o->destroy (obj);
361      o->destroy (obj);
362      o->destroy (obj);
363      o->destroy (obj);
364
365      g_assert (!data[0].freed);
366    }
367  }
368}
369
370
371int
372main (int argc, char **argv)
373{
374  hb_test_init (&argc, &argv);
375
376  hb_test_add (test_object);
377
378  return hb_test_run ();
379}
380