From 0617b1558234673d3924f37541be01b04d36f05a Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 10 May 2011 17:37:08 -0400 Subject: [PATCH] [test] Test blob API --- test/Makefile.am | 1 + test/test-blob.c | 280 +++++++++++++++++++++++++++++++++++++++++++++ test/test-buffer.c | 149 ++++++++++++------------ 3 files changed, 359 insertions(+), 71 deletions(-) create mode 100644 test/test-blob.c diff --git a/test/Makefile.am b/test/Makefile.am index c7447958f..cc4519c66 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -12,6 +12,7 @@ check_PROGRAMS = $(TEST_PROGS) noinst_PROGRAMS = $(TEST_PROGS) TEST_PROGS += \ + test-blob \ test-buffer \ test-common \ test-object \ diff --git a/test/test-blob.c b/test/test-blob.c new file mode 100644 index 000000000..03962fbdf --- /dev/null +++ b/test/test-blob.c @@ -0,0 +1,280 @@ +/* + * 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-blob.h */ + +#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT) + +# define TEST_MMAP 1 + +#ifdef HAVE_SYS_MMAN_H +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#include +#endif /* HAVE_SYS_MMAN_H */ + +#endif + + +static void +test_blob_empty (void) +{ + hb_blob_t *blob; + unsigned int len; + const char *data; + char *data_writable; + + g_assert (hb_blob_is_immutable (hb_blob_get_empty ())); + g_assert (hb_blob_get_empty () != NULL); + + blob = hb_blob_get_empty (); + g_assert (blob == hb_blob_get_empty ()); + + len = hb_blob_get_length (blob); + g_assert_cmpint (len, ==, 0); + + data = hb_blob_get_data (blob, NULL); + g_assert (data == NULL); + + data = hb_blob_get_data (blob, &len); + g_assert (data == NULL); + g_assert_cmpint (len, ==, 0); + + data_writable = hb_blob_get_data_writable (blob, NULL); + g_assert (data == NULL); + + data_writable = hb_blob_get_data_writable (blob, &len); + g_assert (data_writable == NULL); + g_assert_cmpint (len, ==, 0); +} + +static const char test_data[] = "test\0data"; + +static const char *blob_names[] = { + "duplicate", + "readonly", + "writable" +#ifdef TEST_MMAP + , "readonly-may-make-writable" +#endif +}; + +typedef struct +{ + hb_blob_t *blob; + int freed; + char *data; + unsigned int len; +} fixture_t; + +static void +free_up (fixture_t *fixture) +{ + g_assert_cmpint (fixture->freed, ==, 0); + fixture->freed++; +} + +static void +free_up_free (fixture_t *fixture) +{ + free_up (fixture); + free (fixture->data); +} + + +static uintptr_t +get_pagesize (void) +{ + uintptr_t pagesize = -1; + +#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) + pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE); +#elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) + pagesize = (uintptr_t) sysconf (_SC_PAGESIZE); +#elif defined(HAVE_GETPAGESIZE) + pagesize = (uintptr_t) getpagesize (); +#endif + + g_assert (pagesize != (uintptr_t) -1); + + return pagesize; +} + +static void +free_up_munmap (fixture_t *fixture) +{ + free_up (fixture); + munmap (fixture->data, get_pagesize ()); +} + +#include +static void +fixture_init (fixture_t *fixture, gconstpointer user_data) +{ + hb_memory_mode_t mm = (hb_memory_mode_t) GPOINTER_TO_INT (user_data); + unsigned int len; + const char *data; + hb_destroy_func_t free_func; + + switch (GPOINTER_TO_INT (user_data)) + { + case HB_MEMORY_MODE_DUPLICATE: + data = test_data; + len = sizeof (test_data); + free_func = (hb_destroy_func_t) free_up; + break; + + case HB_MEMORY_MODE_READONLY: + data = test_data; + len = sizeof (test_data); + free_func = (hb_destroy_func_t) free_up; + break; + + case HB_MEMORY_MODE_WRITABLE: + data = strndup (test_data, sizeof (test_data)); + len = sizeof (test_data); + free_func = (hb_destroy_func_t) free_up_free; + break; + +#if TEST_MMAP + case HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE: + { + uintptr_t pagesize = get_pagesize (); + + data = mmap (NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + g_assert (data != (char *) -1); + memcpy ((char *) data, test_data, sizeof (test_data)); + mprotect ((char *) data, pagesize, PROT_READ); + len = sizeof (test_data); + free_func = (hb_destroy_func_t) free_up_munmap; + break; + } +#endif + + default: + g_assert_not_reached (); + } + + fixture->freed = 0; + fixture->data = (char *) data; + fixture->len = len; + fixture->blob = hb_blob_create (data, len, mm, fixture, free_func); +} + +static void +fixture_finish (fixture_t *fixture, gconstpointer user_data) +{ + hb_blob_destroy (fixture->blob); + g_assert_cmpint (fixture->freed, ==, 1); +} + + +static void +test_blob (fixture_t *fixture, gconstpointer user_data) +{ + hb_blob_t *b = fixture->blob; + hb_memory_mode_t mm = GPOINTER_TO_INT (user_data); + unsigned int len; + const char *data; + char *data_writable; + unsigned int i; + + g_assert (b); + + len = hb_blob_get_length (b); + g_assert_cmpint (len, ==, fixture->len); + + data = hb_blob_get_data (b, &len); + g_assert_cmpint (len, ==, fixture->len); + if (mm == HB_MEMORY_MODE_DUPLICATE) { + g_assert (data != fixture->data); + g_assert_cmpint (fixture->freed, ==, 1); + mm = HB_MEMORY_MODE_WRITABLE; + } else { + g_assert (data == fixture->data); + g_assert_cmpint (fixture->freed, ==, 0); + } + + data_writable = hb_blob_get_data_writable (b, &len); + g_assert_cmpint (len, ==, fixture->len); + g_assert (data_writable); + g_assert (0 == memcmp (data_writable, fixture->data, fixture->len)); + if (mm == HB_MEMORY_MODE_READONLY) { + g_assert (data_writable != data); + g_assert_cmpint (fixture->freed, ==, 1); + } else { + g_assert (data_writable == data); + } + + data = hb_blob_get_data (b, &len); + g_assert_cmpint (len, ==, fixture->len); + g_assert (data == data_writable); + + memset (data_writable, 0, fixture->len); + + /* Now, make it immutable and watch get_data_writable() fail */ + + g_assert (!hb_blob_is_immutable (b)); + hb_blob_make_immutable (b); + g_assert (hb_blob_is_immutable (b)); + + data_writable = hb_blob_get_data_writable (b, &len); + g_assert (!data_writable); + g_assert_cmpint (len, ==, 0); + + data = hb_blob_get_data (b, &len); + g_assert_cmpint (len, ==, fixture->len); + for (i = 0; i < len; i++) + g_assert ('\0' == data[i]); +} + + +int +main (int argc, char **argv) +{ + unsigned int i; + + hb_test_init (&argc, &argv); + + hb_test_add (test_blob_empty); + + for (i = 0; i < G_N_ELEMENTS (blob_names); i++) + { + const void *blob_type = GINT_TO_POINTER (i); + const char *blob_name = blob_names[i]; + + hb_test_add_fixture_flavor (fixture, blob_type, blob_name, test_blob); + } + + /* + * create_sub_blob + */ + + return hb_test_run (); +} diff --git a/test/test-buffer.c b/test/test-buffer.c index 3f0eb565d..2b1c7b788 100644 --- a/test/test-buffer.c +++ b/test/test-buffer.c @@ -53,35 +53,37 @@ static const char *buffer_names[] = { typedef struct { - hb_buffer_t *b; + hb_buffer_t *buffer; } fixture_t; static void fixture_init (fixture_t *fixture, gconstpointer user_data) { + hb_buffer_t *b; unsigned int i; - fixture->b = hb_buffer_create (0); + b = fixture->buffer = hb_buffer_create (0); - switch (GPOINTER_TO_INT (user_data)) { + switch (GPOINTER_TO_INT (user_data)) + { case BUFFER_EMPTY: break; case BUFFER_ONE_BY_ONE: for (i = 1; i < G_N_ELEMENTS (utf32) - 1; i++) - hb_buffer_add (fixture->b, utf32[i], 1, i); + hb_buffer_add (b, utf32[i], 1, i); break; case BUFFER_UTF32: - hb_buffer_add_utf32 (fixture->b, utf32, G_N_ELEMENTS (utf32), 1, G_N_ELEMENTS (utf32) - 2); + hb_buffer_add_utf32 (b, utf32, G_N_ELEMENTS (utf32), 1, G_N_ELEMENTS (utf32) - 2); break; case BUFFER_UTF16: - hb_buffer_add_utf16 (fixture->b, utf16, G_N_ELEMENTS (utf16), 1, G_N_ELEMENTS (utf16) - 2); + hb_buffer_add_utf16 (b, utf16, G_N_ELEMENTS (utf16), 1, G_N_ELEMENTS (utf16) - 2); break; case BUFFER_UTF8: - hb_buffer_add_utf8 (fixture->b, utf8, G_N_ELEMENTS (utf8), 1, G_N_ELEMENTS (utf8) - 2); + hb_buffer_add_utf8 (b, utf8, G_N_ELEMENTS (utf8), 1, G_N_ELEMENTS (utf8) - 2); break; default: @@ -92,64 +94,66 @@ fixture_init (fixture_t *fixture, gconstpointer user_data) static void fixture_finish (fixture_t *fixture, gconstpointer user_data) { - hb_buffer_destroy (fixture->b); + hb_buffer_destroy (fixture->buffer); } static void test_buffer_properties (fixture_t *fixture, gconstpointer user_data) { + hb_buffer_t *b = fixture->buffer; hb_unicode_funcs_t *ufuncs; /* test default properties */ - g_assert (hb_buffer_get_unicode_funcs (fixture->b) == hb_unicode_funcs_get_default ()); - g_assert (hb_buffer_get_direction (fixture->b) == HB_DIRECTION_INVALID); - g_assert (hb_buffer_get_script (fixture->b) == HB_SCRIPT_INVALID); - g_assert (hb_buffer_get_language (fixture->b) == NULL); + g_assert (hb_buffer_get_unicode_funcs (b) == hb_unicode_funcs_get_default ()); + g_assert (hb_buffer_get_direction (b) == HB_DIRECTION_INVALID); + g_assert (hb_buffer_get_script (b) == HB_SCRIPT_INVALID); + g_assert (hb_buffer_get_language (b) == NULL); /* test property changes are retained */ ufuncs = hb_unicode_funcs_create (NULL); - hb_buffer_set_unicode_funcs (fixture->b, ufuncs); + hb_buffer_set_unicode_funcs (b, ufuncs); hb_unicode_funcs_destroy (ufuncs); - g_assert (hb_buffer_get_unicode_funcs (fixture->b) == ufuncs); + g_assert (hb_buffer_get_unicode_funcs (b) == ufuncs); - hb_buffer_set_direction (fixture->b, HB_DIRECTION_RTL); - g_assert (hb_buffer_get_direction (fixture->b) == HB_DIRECTION_RTL); + hb_buffer_set_direction (b, HB_DIRECTION_RTL); + g_assert (hb_buffer_get_direction (b) == HB_DIRECTION_RTL); - hb_buffer_set_script (fixture->b, HB_SCRIPT_ARABIC); - g_assert (hb_buffer_get_script (fixture->b) == HB_SCRIPT_ARABIC); + hb_buffer_set_script (b, HB_SCRIPT_ARABIC); + g_assert (hb_buffer_get_script (b) == HB_SCRIPT_ARABIC); - hb_buffer_set_language (fixture->b, hb_language_from_string ("fa")); - g_assert (hb_buffer_get_language (fixture->b) == hb_language_from_string ("Fa")); + hb_buffer_set_language (b, hb_language_from_string ("fa")); + g_assert (hb_buffer_get_language (b) == hb_language_from_string ("Fa")); /* test reset clears properties */ - hb_buffer_reset (fixture->b); + hb_buffer_reset (b); - g_assert (hb_buffer_get_unicode_funcs (fixture->b) == hb_unicode_funcs_get_default ()); - g_assert (hb_buffer_get_direction (fixture->b) == HB_DIRECTION_INVALID); - g_assert (hb_buffer_get_script (fixture->b) == HB_SCRIPT_INVALID); - g_assert (hb_buffer_get_language (fixture->b) == NULL); + g_assert (hb_buffer_get_unicode_funcs (b) == hb_unicode_funcs_get_default ()); + g_assert (hb_buffer_get_direction (b) == HB_DIRECTION_INVALID); + g_assert (hb_buffer_get_script (b) == HB_SCRIPT_INVALID); + g_assert (hb_buffer_get_language (b) == NULL); } static void test_buffer_contents (fixture_t *fixture, gconstpointer user_data) { + hb_buffer_t *b = fixture->buffer; unsigned int i, len, len2; buffer_type_t buffer_type = GPOINTER_TO_INT (user_data); hb_glyph_info_t *glyphs; if (buffer_type == BUFFER_EMPTY) { - g_assert_cmpint (hb_buffer_get_length (fixture->b), ==, 0); + g_assert_cmpint (hb_buffer_get_length (b), ==, 0); return; } - len = hb_buffer_get_length (fixture->b); - glyphs = hb_buffer_get_glyph_infos (fixture->b, NULL); /* test NULL */ - glyphs = hb_buffer_get_glyph_infos (fixture->b, &len2); + len = hb_buffer_get_length (b); + glyphs = hb_buffer_get_glyph_infos (b, NULL); /* test NULL */ + glyphs = hb_buffer_get_glyph_infos (b, &len2); g_assert_cmpint (len, ==, len2); g_assert_cmpint (len, ==, 5); @@ -174,22 +178,22 @@ test_buffer_contents (fixture_t *fixture, gconstpointer user_data) /* reverse, test, and reverse back */ - hb_buffer_reverse (fixture->b); + hb_buffer_reverse (b); for (i = 0; i < len; i++) g_assert_cmphex (glyphs[i].codepoint, ==, utf32[len-i]); - hb_buffer_reverse (fixture->b); + hb_buffer_reverse (b); for (i = 0; i < len; i++) g_assert_cmphex (glyphs[i].codepoint, ==, utf32[1+i]); /* reverse_clusters works same as reverse for now since each codepoint is * in its own cluster */ - hb_buffer_reverse_clusters (fixture->b); + hb_buffer_reverse_clusters (b); for (i = 0; i < len; i++) g_assert_cmphex (glyphs[i].codepoint, ==, utf32[len-i]); - hb_buffer_reverse_clusters (fixture->b); + hb_buffer_reverse_clusters (b); for (i = 0; i < len; i++) g_assert_cmphex (glyphs[i].codepoint, ==, utf32[1+i]); @@ -198,18 +202,18 @@ test_buffer_contents (fixture_t *fixture, gconstpointer user_data) /* reverse, test, and reverse back */ - hb_buffer_reverse (fixture->b); + hb_buffer_reverse (b); for (i = 0; i < len; i++) g_assert_cmphex (glyphs[i].codepoint, ==, utf32[len-i]); - hb_buffer_reverse (fixture->b); + hb_buffer_reverse (b); for (i = 0; i < len; i++) g_assert_cmphex (glyphs[i].codepoint, ==, utf32[1+i]); /* reverse_clusters twice still should return the original string, * but when applied once, the 1-2 cluster should be retained. */ - hb_buffer_reverse_clusters (fixture->b); + hb_buffer_reverse_clusters (b); for (i = 0; i < len; i++) { unsigned int j = len-1-i; if (j == 1) @@ -219,7 +223,7 @@ test_buffer_contents (fixture_t *fixture, gconstpointer user_data) g_assert_cmphex (glyphs[i].codepoint, ==, utf32[1+j]); } - hb_buffer_reverse_clusters (fixture->b); + hb_buffer_reverse_clusters (b); for (i = 0; i < len; i++) g_assert_cmphex (glyphs[i].codepoint, ==, utf32[1+i]); @@ -227,40 +231,41 @@ test_buffer_contents (fixture_t *fixture, gconstpointer user_data) /* test setting length */ /* enlarge */ - g_assert (hb_buffer_set_length (fixture->b, 10)); - glyphs = hb_buffer_get_glyph_infos (fixture->b, NULL); - g_assert_cmpint (hb_buffer_get_length (fixture->b), ==, 10); + g_assert (hb_buffer_set_length (b, 10)); + glyphs = hb_buffer_get_glyph_infos (b, NULL); + g_assert_cmpint (hb_buffer_get_length (b), ==, 10); for (i = 0; i < 5; i++) g_assert_cmphex (glyphs[i].codepoint, ==, utf32[1+i]); for (i = 5; i < 10; i++) g_assert_cmphex (glyphs[i].codepoint, ==, 0); /* shrink */ - g_assert (hb_buffer_set_length (fixture->b, 3)); - glyphs = hb_buffer_get_glyph_infos (fixture->b, NULL); - g_assert_cmpint (hb_buffer_get_length (fixture->b), ==, 3); + g_assert (hb_buffer_set_length (b, 3)); + glyphs = hb_buffer_get_glyph_infos (b, NULL); + g_assert_cmpint (hb_buffer_get_length (b), ==, 3); for (i = 0; i < 3; i++) g_assert_cmphex (glyphs[i].codepoint, ==, utf32[1+i]); - g_assert (hb_buffer_allocation_successful (fixture->b)); + g_assert (hb_buffer_allocation_successful (b)); /* test reset clears content */ - hb_buffer_reset (fixture->b); - g_assert_cmpint (hb_buffer_get_length (fixture->b), ==, 0); + hb_buffer_reset (b); + g_assert_cmpint (hb_buffer_get_length (b), ==, 0); } static void test_buffer_positions (fixture_t *fixture, gconstpointer user_data) { + hb_buffer_t *b = fixture->buffer; unsigned int i, len, len2; hb_glyph_position_t *positions; /* Without shaping, positions should all be zero */ - len = hb_buffer_get_length (fixture->b); - positions = hb_buffer_get_glyph_positions (fixture->b, NULL); /* test NULL */ - positions = hb_buffer_get_glyph_positions (fixture->b, &len2); + len = hb_buffer_get_length (b); + positions = hb_buffer_get_glyph_positions (b, NULL); /* test NULL */ + positions = hb_buffer_get_glyph_positions (b, &len2); g_assert_cmpint (len, ==, len2); for (i = 0; i < len; i++) { g_assert_cmpint (0, ==, positions[i].x_advance); @@ -271,49 +276,51 @@ test_buffer_positions (fixture_t *fixture, gconstpointer user_data) } /* test reset clears content */ - hb_buffer_reset (fixture->b); - g_assert_cmpint (hb_buffer_get_length (fixture->b), ==, 0); + hb_buffer_reset (b); + g_assert_cmpint (hb_buffer_get_length (b), ==, 0); } static void test_buffer_allocation (fixture_t *fixture, gconstpointer user_data) { - g_assert_cmpint (hb_buffer_get_length (fixture->b), ==, 0); + hb_buffer_t *b = fixture->buffer; - g_assert (hb_buffer_pre_allocate (fixture->b, 100)); - g_assert_cmpint (hb_buffer_get_length (fixture->b), ==, 0); - g_assert (hb_buffer_allocation_successful (fixture->b)); + g_assert_cmpint (hb_buffer_get_length (b), ==, 0); + + g_assert (hb_buffer_pre_allocate (b, 100)); + g_assert_cmpint (hb_buffer_get_length (b), ==, 0); + g_assert (hb_buffer_allocation_successful (b)); /* lets try a huge allocation, make sure it fails */ - g_assert (!hb_buffer_pre_allocate (fixture->b, (unsigned int) -1)); - g_assert_cmpint (hb_buffer_get_length (fixture->b), ==, 0); - g_assert (!hb_buffer_allocation_successful (fixture->b)); + g_assert (!hb_buffer_pre_allocate (b, (unsigned int) -1)); + g_assert_cmpint (hb_buffer_get_length (b), ==, 0); + g_assert (!hb_buffer_allocation_successful (b)); /* small one again */ - g_assert (hb_buffer_pre_allocate (fixture->b, 50)); - g_assert_cmpint (hb_buffer_get_length (fixture->b), ==, 0); - g_assert (!hb_buffer_allocation_successful (fixture->b)); + g_assert (hb_buffer_pre_allocate (b, 50)); + g_assert_cmpint (hb_buffer_get_length (b), ==, 0); + g_assert (!hb_buffer_allocation_successful (b)); - hb_buffer_reset (fixture->b); - g_assert (hb_buffer_allocation_successful (fixture->b)); + hb_buffer_reset (b); + g_assert (hb_buffer_allocation_successful (b)); /* all allocation and size */ - g_assert (!hb_buffer_pre_allocate (fixture->b, ((unsigned int) -1) / 20 + 1)); - g_assert (!hb_buffer_allocation_successful (fixture->b)); + g_assert (!hb_buffer_pre_allocate (b, ((unsigned int) -1) / 20 + 1)); + g_assert (!hb_buffer_allocation_successful (b)); - hb_buffer_reset (fixture->b); - g_assert (hb_buffer_allocation_successful (fixture->b)); + hb_buffer_reset (b); + g_assert (hb_buffer_allocation_successful (b)); /* technically, this one can actually pass on 64bit machines, but * I'm doubtful that any malloc allows 4GB allocations at a time. * But let's only enable it on a 32-bit machine. */ if (sizeof (long) == 4) { - g_assert (!hb_buffer_pre_allocate (fixture->b, ((unsigned int) -1) / 20 - 1)); - g_assert (!hb_buffer_allocation_successful (fixture->b)); + g_assert (!hb_buffer_pre_allocate (b, ((unsigned int) -1) / 20 - 1)); + g_assert (!hb_buffer_allocation_successful (b)); } - hb_buffer_reset (fixture->b); - g_assert (hb_buffer_allocation_successful (fixture->b)); + hb_buffer_reset (b); + g_assert (hb_buffer_allocation_successful (b)); }