Revealed many bugs in the (untested and known buggy) user_data support.pull/1/head
parent
f74d6c81f1
commit
db99589529
2 changed files with 320 additions and 0 deletions
@ -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 (); |
||||
} |
Loading…
Reference in new issue