HarfBuzz text shaping engine
http://harfbuzz.github.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
379 lines
10 KiB
379 lines
10 KiB
/* |
|
* 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 */ |
|
|
|
|
|
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_create (NULL, 0, HB_MEMORY_MODE_DUPLICATE, NULL, NULL); |
|
} |
|
|
|
static void * |
|
create_buffer (void) |
|
{ |
|
return hb_buffer_create (); |
|
} |
|
static void * |
|
create_buffer_inert (void) |
|
{ |
|
return NULL; |
|
} |
|
|
|
static void * |
|
create_set (void) |
|
{ |
|
return hb_set_create (); |
|
} |
|
static void * |
|
create_set_inert (void) |
|
{ |
|
return NULL; |
|
} |
|
|
|
static void * |
|
create_face (void) |
|
{ |
|
hb_blob_t *blob = (hb_blob_t *) create_blob (); |
|
hb_face_t *face = hb_face_create (blob, 0); |
|
hb_blob_destroy (blob); |
|
return face; |
|
} |
|
static void * |
|
create_face_inert (void) |
|
{ |
|
return hb_face_create (hb_blob_get_empty (), 0); |
|
} |
|
|
|
static void * |
|
create_font (void) |
|
{ |
|
hb_face_t *face = (hb_face_t *) create_face (); |
|
hb_font_t *font = hb_font_create (face); |
|
hb_face_destroy (face); |
|
return font; |
|
} |
|
static void * |
|
create_font_inert (void) |
|
{ |
|
return hb_font_create (hb_face_get_empty ()); |
|
} |
|
|
|
static void * |
|
create_font_funcs (void) |
|
{ |
|
return hb_font_funcs_create (); |
|
} |
|
static void * |
|
create_font_funcs_inert (void) |
|
{ |
|
return NULL; |
|
} |
|
|
|
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, hb_bool_t replace); |
|
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; |
|
create_func_t get_empty; |
|
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, \ |
|
(create_func_t) hb_##name##_get_empty, \ |
|
(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, \ |
|
(create_func_t) hb_##name##_get_empty, \ |
|
(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 (buffer), |
|
OBJECT_WITHOUT_IMMUTABILITY (set), |
|
OBJECT_WITH_IMMUTABILITY (blob), |
|
OBJECT_WITH_IMMUTABILITY (face), |
|
OBJECT_WITH_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; |
|
} |
|
|
|
|
|
typedef struct { |
|
const object_t *klass; |
|
void *object; |
|
hb_user_data_key_t key; |
|
} deadlock_test_t; |
|
|
|
static void free_deadlock_test (void *p) |
|
{ |
|
deadlock_test_t *t = (deadlock_test_t *) p; |
|
|
|
g_assert (NULL == t->klass->get_user_data (t->object, &t->key)); |
|
} |
|
|
|
|
|
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[1001]; |
|
|
|
{ |
|
unsigned int j; |
|
data_t data[2] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}}; |
|
deadlock_test_t deadlock_test; |
|
|
|
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, TRUE)); |
|
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, TRUE)); |
|
g_assert (o->get_user_data (obj, &key[1]) == &data[1]); |
|
|
|
g_assert (!o->set_user_data (obj, NULL, &data[0], free_up0, TRUE)); |
|
g_assert (o->get_user_data (obj, &key[0]) == &data[0]); |
|
g_assert (o->set_user_data (obj, &key[0], &data[1], NULL, TRUE)); |
|
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, TRUE)); |
|
g_assert (!data[0].freed); |
|
g_assert (o->set_user_data (obj, &key[0], NULL, NULL, TRUE)); |
|
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, TRUE)); |
|
g_assert (!o->set_user_data (obj, &key[0], &data[0], free_up0, FALSE)); |
|
g_assert_cmpuint (global_data, ==, 0); |
|
g_assert (o->set_user_data (obj, &key[0], NULL, global_free_up, TRUE)); |
|
g_assert_cmpuint (global_data, ==, 0); |
|
g_assert (o->set_user_data (obj, &key[0], NULL, NULL, TRUE)); |
|
g_assert_cmpuint (global_data, ==, 1); |
|
|
|
global_data = 0; |
|
for (j = 2; j < 1000; j++) |
|
g_assert (o->set_user_data (obj, &key[j], &data[j], global_free_up, TRUE)); |
|
for (j = 2; j < 1000; j++) |
|
g_assert (o->get_user_data (obj, &key[j]) == &data[j]); |
|
for (j = 100; j < 1000; j++) |
|
g_assert (o->set_user_data (obj, &key[j], NULL, NULL, TRUE)); |
|
for (j = 2; j < 100; j++) |
|
g_assert (o->get_user_data (obj, &key[j]) == &data[j]); |
|
for (j = 100; j < 1000; j++) |
|
g_assert (!o->get_user_data (obj, &key[j])); |
|
g_assert_cmpuint (global_data, ==, 900); |
|
|
|
/* Test set_user_data where the destroy() func calls user_data functions. |
|
* Make sure it doesn't deadlock or corrupt memory. */ |
|
deadlock_test.klass = o; |
|
deadlock_test.object = obj; |
|
g_assert (o->set_user_data (obj, &deadlock_test.key, &deadlock_test, free_deadlock_test, TRUE)); |
|
g_assert (o->set_user_data (obj, &deadlock_test.key, NULL, NULL, TRUE)); |
|
|
|
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 ("->get_empty()"); |
|
obj = o->get_empty (); |
|
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, TRUE)); |
|
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); |
|
} |
|
|
|
{ |
|
data_t data[2] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}}; |
|
|
|
g_test_message ("->create_inert()"); |
|
obj = o->create_inert (); |
|
if (!obj) |
|
continue; |
|
if (obj == o->get_empty ()) |
|
continue; /* Tested already */ |
|
|
|
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, TRUE)); |
|
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 (); |
|
}
|
|
|