Rename test/ to test/api/
diff --git a/test/api/Makefile.am b/test/api/Makefile.am new file mode 100644 index 0000000..b3c8cc2 --- /dev/null +++ b/test/api/Makefile.am
@@ -0,0 +1,131 @@ +# Process this file with automake to produce Makefile.in + +NULL = +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = +MAINTAINERCLEANFILES = + +if HAVE_GLIB +AM_CPPFLAGS = -DSRCDIR="\"$(srcdir)\"" -I$(top_srcdir)/src/ -I$(top_builddir)/src/ $(GLIB_CFLAGS) $(GTHREAD_CFLAGS) +LDADD = $(top_builddir)/src/libharfbuzz.la $(GLIB_LIBS) $(GTHREAD_LIBS) + +EXTRA_DIST += hb-test.h + +check_PROGRAMS = $(TEST_PROGS) +noinst_PROGRAMS = $(TEST_PROGS) + +TEST_PROGS = \ + test-blob \ + test-buffer \ + test-common \ + test-font \ + test-object \ + test-shape \ + test-unicode \ + test-version \ + $(NULL) + +if HAVE_OT +TEST_PROGS += \ + test-ot-tag \ + $(NULL) +endif + +# Tests for header compilation +TEST_PROGS += \ + test-c \ + test-cplusplus \ + $(NULL) +test_cplusplus_SOURCES = test-cplusplus.cc +test_c_CPPFLAGS = $(AM_CPPFLAGS) +test_cplusplus_CPPFLAGS = $(AM_CPPFLAGS) +if HAVE_ICU +test_c_CPPFLAGS += $(ICU_CFLAGS) +test_cplusplus_CPPFLAGS += $(ICU_CFLAGS) +endif +if HAVE_FREETYPE +test_c_CPPFLAGS += $(FREETYPE_CFLAGS) +test_cplusplus_CPPFLAGS += $(FREETYPE_CFLAGS) +endif + +if HAVE_FREETYPE +TEST_PROGS += test-shape-complex +test_shape_complex_CPPFLAGS = $(AM_CPPFLAGS) $(FREETYPE_CFLAGS) +test_shape_complex_LDADD = $(LDADD) $(FREETYPE_LIBS) +endif + + +# Default test running environment +TESTS = $(TEST_PROGS) +TESTS_ENVIRONMENT = \ + MALLOC_CHECK_=2 \ + MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) \ + G_DEBUG=gc-friendly \ + G_SLICE=always-malloc \ + srcdir=$(srcdir) \ + $(ENV) + + +# check-tool: Run tests under $(TOOL) +check-tool: + $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) check \ + TESTS_ENVIRONMENT='$(TESTS_ENVIRONMENT) $(top_builddir)/libtool --mode=execute \ + env $(TOOL)' +# check-tool-raw: Run tests under $(TOOL), but don't run under libtool +check-tool-raw: + $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) check \ + TESTS_ENVIRONMENT='$(TESTS_ENVIRONMENT) \ + env $(TOOL)' + +# check-gtester: Run tests under gtester +GTESTER = gtester +check-gtester: + $(AM_V_at)$(MAKE) $(AM_MAKEFLGS) check-tool-raw TOOL="$(GTESTER) --verbose --keep-going" + + +# Check tests under valgrind. Saves log to log-valgrind.txt +VALGRIND_FLAGS = \ + --tool=memcheck --suppressions=$(srcdir)/.valgrind-suppressions \ + --track-origins=yes \ + --leak-check=yes + $(EXTRA_VALGRIND_FLAGS) +# Can't do for now: --show-reachable=yes +CLEANFILES += log-valgrind.txt +valgrind_verbose = $(valgrind_verbose_$(V)) +valgrind_verbose_ = $(valgrind_verbose_$(AM_DEFAULT_VERBOSITY)) +valgrind_verbose_0 = | \ + grep '\(^[^=]\|ERROR SUMMARY\|definitely lost\|indirectly lost\)' | grep -v ': 0' +# TODO: The following check does not fail if valgrind finds error. It should. +check-valgrind: + $(AM_V_at)$(MAKE) $(AM_MAKEFLGS) check-tool TOOL="valgrind $(VALGRIND_FLAGS)" \ + 2>&1 | tee log-valgrind.txt $(valgrind_verbose) + + +# check-symbols: Finds untested API symbols +symbols-tested.txt: $(TEST_PROGS) + $(AM_V_GEN)$(top_builddir)/libtool --mode=execute nm $^ \ + | grep ' U hb_' | sed 's/.* U hb_/hb_/' \ + | sort | uniq > $@.tmp && mv $@.tmp $@ +symbols-exported.txt: $(top_builddir)/src/.libs/libharfbuzz.so + $(AM_V_GEN)$(top_builddir)/libtool --mode=execute nm $^ \ + | grep ' T ' | sed 's/.* T //' | grep -v '^\(_init\|_fini\)$$' \ + | sort | uniq > $@.tmp && mv $@.tmp $@ +symbols-untested.txt: symbols-tested.txt symbols-exported.txt + $(AM_V_GEN)diff $^ > $@.tmp; mv $@.tmp $@ +CLEANFILES += symbols-tested.txt symbols-exported.txt symbols-untested.txt +check-symbols: symbols-untested.txt + @! cat $^ | grep . + + + +else +check-am: err-glib +err-glib: + @echo "You need to have glib support enabled to run the tests" + @exit 77 +endif + +.PHONY: check-symbols check-tool check-valgrind + +-include $(top_srcdir)/git.mk
diff --git a/test/api/hb-test.h b/test/api/hb-test.h new file mode 100644 index 0000000..f36d53b --- /dev/null +++ b/test/api/hb-test.h
@@ -0,0 +1,265 @@ +/* + * 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 + */ + +#ifndef HB_TEST_H +#define HB_TEST_H + +#include <config.h> + +#include <hb-glib.h> + +#include <stdlib.h> +#include <string.h> + +HB_BEGIN_DECLS + +/* Just in case */ +#undef G_DISABLE_ASSERT + + +/* Misc */ + +/* This is too ugly to be public API, but quite handy. */ +#define HB_TAG_CHAR4(s) (HB_TAG(((const char *) s)[0], \ + ((const char *) s)[1], \ + ((const char *) s)[2], \ + ((const char *) s)[3])) + + +static inline const char * +srcdir (void) +{ + static const char *s; + + if (!s) { + s = getenv ("srcdir"); + +#ifdef SRCDIR + if (!s || !s[0]) + s = SRCDIR; +#endif + + if (!s || !s[0]) + s = "."; + } + + return s; +} + + +/* Helpers */ + +static inline void +hb_test_init (int *argc, char ***argv) +{ + g_thread_init (NULL); + g_test_init (argc, argv, NULL); +} + +static inline int +hb_test_run (void) +{ + return g_test_run (); +} + + +/* Bugzilla helpers */ + +static inline void +hb_test_bug (const char *uri_base, unsigned int number) +{ + char *s = g_strdup_printf ("%u", number); + + g_test_bug_base (uri_base); + g_test_bug (s); + + g_free (s); +} + +static inline void +hb_test_bug_freedesktop (unsigned int number) +{ + hb_test_bug ("http://bugs.freedesktop.org/", number); +} + +static inline void +hb_test_bug_gnome (unsigned int number) +{ + hb_test_bug ("http://bugzilla.gnome.org/", number); +} + +static inline void +hb_test_bug_mozilla (unsigned int number) +{ + hb_test_bug ("http://bugzilla.mozilla.org/", number); +} + +static inline void +hb_test_bug_redhat (unsigned int number) +{ + hb_test_bug ("http://bugzilla.redhat.com/", number); +} + + +/* Wrap glib test functions to simplify. Should have been in glib already. */ + +/* Drops the "test_" prefix and converts '_' to '/'. + * Essentially builds test path from function name. */ +static inline char * +hb_test_normalize_path (const char *path) +{ + char *s, *p; + + g_assert (0 == strncmp (path, "test_", 5)); + path += 4; + + s = g_strdup (path); + for (p = s; *p; p++) + if (*p == '_') + *p = '/'; + + return s; +} + + +#if GLIB_CHECK_VERSION(2,25,12) +typedef GTestFunc hb_test_func_t; +typedef GTestDataFunc hb_test_data_func_t; +typedef GTestFixtureFunc hb_test_fixture_func_t; +#else +typedef void (*hb_test_func_t) (void); +typedef void (*hb_test_data_func_t) (gconstpointer user_data); +typedef void (*hb_test_fixture_func_t) (void); +#endif + +#if !GLIB_CHECK_VERSION(2,30,0) +#define g_test_fail() g_error("Test failed") +#endif + +static inline void +hb_test_add_func (const char *test_path, + hb_test_func_t test_func) +{ + char *normal_path = hb_test_normalize_path (test_path); + g_test_add_func (normal_path, test_func); + g_free (normal_path); +} +#define hb_test_add(Func) hb_test_add_func (#Func, Func) + +static inline void +hb_test_add_func_flavor (const char *test_path, + const char *flavor, + hb_test_func_t test_func) +{ + char *path = g_strdup_printf ("%s/%s", test_path, flavor); + hb_test_add_func (path, test_func); + g_free (path); +} +#define hb_test_add_flavor(Flavor, Func) hb_test_add_func (#Func, Flavor, Func) + +static inline void +hb_test_add_data_func (const char *test_path, + gconstpointer test_data, + hb_test_data_func_t test_func) +{ + char *normal_path = hb_test_normalize_path (test_path); + g_test_add_data_func (normal_path, test_data, test_func); + g_free (normal_path); +} +#define hb_test_add_data(UserData, Func) hb_test_add_data_func (#Func, UserData, Func) + +static inline void +hb_test_add_data_func_flavor (const char *test_path, + const char *flavor, + gconstpointer test_data, + hb_test_data_func_t test_func) +{ + char *path = g_strdup_printf ("%s/%s", test_path, flavor); + hb_test_add_data_func (path, test_data, test_func); + g_free (path); +} +#define hb_test_add_data_flavor(UserData, Flavor, Func) hb_test_add_data_func_flavor (#Func, Flavor, UserData, Func) + + +static inline void +hb_test_add_vtable (const char *test_path, + gsize data_size, + gconstpointer test_data, + hb_test_fixture_func_t data_setup, + hb_test_fixture_func_t data_test, + hb_test_fixture_func_t data_teardown) +{ + char *normal_path = hb_test_normalize_path (test_path); + g_test_add_vtable (normal_path, data_size, test_data, data_setup, data_test, data_teardown); + g_free (normal_path); +} +#define hb_test_add_fixture(FixturePrefix, UserData, Func) \ +G_STMT_START { \ + typedef G_PASTE (FixturePrefix, _t) Fixture; \ + void (*add_vtable) (const char*, gsize, gconstpointer, \ + void (*) (Fixture*, gconstpointer), \ + void (*) (Fixture*, gconstpointer), \ + void (*) (Fixture*, gconstpointer)) \ + = (void (*) (const gchar *, gsize, gconstpointer, \ + void (*) (Fixture*, gconstpointer), \ + void (*) (Fixture*, gconstpointer), \ + void (*) (Fixture*, gconstpointer))) hb_test_add_vtable; \ + add_vtable (#Func, sizeof (G_PASTE (FixturePrefix, _t)), UserData, \ + G_PASTE (FixturePrefix, _init), Func, G_PASTE (FixturePrefix, _finish)); \ +} G_STMT_END + +static inline void +hb_test_add_vtable_flavor (const char *test_path, + const char *flavor, + gsize data_size, + gconstpointer test_data, + hb_test_fixture_func_t data_setup, + hb_test_fixture_func_t data_test, + hb_test_fixture_func_t data_teardown) +{ + char *path = g_strdup_printf ("%s/%s", test_path, flavor); + hb_test_add_vtable (path, data_size, test_data, data_setup, data_test, data_teardown); + g_free (path); +} +#define hb_test_add_fixture_flavor(FixturePrefix, UserData, Flavor, Func) \ +G_STMT_START { \ + typedef G_PASTE (FixturePrefix, _t) Fixture; \ + void (*add_vtable) (const char*, const char *, gsize, gconstpointer, \ + void (*) (Fixture*, gconstpointer), \ + void (*) (Fixture*, gconstpointer), \ + void (*) (Fixture*, gconstpointer)) \ + = (void (*) (const gchar *, const char *, gsize, gconstpointer, \ + void (*) (Fixture*, gconstpointer), \ + void (*) (Fixture*, gconstpointer), \ + void (*) (Fixture*, gconstpointer))) hb_test_add_vtable_flavor; \ + add_vtable (#Func, Flavor, sizeof (G_PASTE (FixturePrefix, _t)), UserData, \ + G_PASTE (FixturePrefix, _init), Func, G_PASTE (FixturePrefix, _finish)); \ +} G_STMT_END + + +HB_END_DECLS + +#endif /* HB_TEST_H */
diff --git a/test/api/test-blob.c b/test/api/test-blob.c new file mode 100644 index 0000000..5fcb208 --- /dev/null +++ b/test/api/test-blob.c
@@ -0,0 +1,301 @@ +/* + * 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) && defined(HAVE_MMAP) + +# define TEST_MMAP 1 + +#ifdef HAVE_SYS_MMAN_H +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#include <sys/mman.h> +#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); + g_assert (hb_blob_get_empty () == hb_blob_create (NULL, 0, HB_MEMORY_MODE_READONLY, NULL, 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); +} + + +#ifdef TEST_MMAP +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 ()); +} +#endif + +#include <errno.h> +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 = malloc (sizeof (test_data)); + memcpy ((char *) data, test_data, sizeof (test_data)); + len = sizeof (test_data); + free_func = (hb_destroy_func_t) free_up_free; + break; + +#ifdef 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_ANON, -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]); +} + +static void +test_blob_subblob (fixture_t *fixture, gconstpointer user_data) +{ + hb_blob_t *b = fixture->blob; + + fixture->len -= 2; + fixture->data++; + fixture->blob = hb_blob_create_sub_blob (b, 1, fixture->len); + hb_blob_destroy (b); + + test_blob (fixture, user_data); + + fixture->data--; + fixture->len += 2; +} + + +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); + hb_test_add_fixture_flavor (fixture, blob_type, blob_name, test_blob_subblob); + } + + /* + * create_sub_blob + */ + + return hb_test_run (); +}
diff --git a/test/api/test-buffer.c b/test/api/test-buffer.c new file mode 100644 index 0000000..ab818d0 --- /dev/null +++ b/test/api/test-buffer.c
@@ -0,0 +1,783 @@ +/* + * 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-buffer.h */ + + +static const char utf8[10] = "ab\360\240\200\200defg"; +static const uint16_t utf16[8] = {'a', 'b', 0xD840, 0xDC00, 'd', 'e', 'f', 'g'}; +static const uint32_t utf32[7] = {'a', 'b', 0x20000, 'd', 'e', 'f', 'g'}; + + +typedef enum { + BUFFER_EMPTY, + BUFFER_ONE_BY_ONE, + BUFFER_UTF32, + BUFFER_UTF16, + BUFFER_UTF8, + BUFFER_NUM_TYPES, +} buffer_type_t; + +static const char *buffer_names[] = { + "empty", + "one-by-one", + "utf32", + "utf16", + "utf8" +}; + +typedef struct +{ + hb_buffer_t *buffer; +} fixture_t; + +static void +fixture_init (fixture_t *fixture, gconstpointer user_data) +{ + hb_buffer_t *b; + unsigned int i; + + b = fixture->buffer = hb_buffer_create (); + + 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 (b, utf32[i], 1, i); + break; + + case BUFFER_UTF32: + hb_buffer_add_utf32 (b, utf32, G_N_ELEMENTS (utf32), 1, G_N_ELEMENTS (utf32) - 2); + break; + + case BUFFER_UTF16: + hb_buffer_add_utf16 (b, utf16, G_N_ELEMENTS (utf16), 1, G_N_ELEMENTS (utf16) - 2); + break; + + case BUFFER_UTF8: + hb_buffer_add_utf8 (b, utf8, G_N_ELEMENTS (utf8), 1, G_N_ELEMENTS (utf8) - 2); + break; + + default: + g_assert_not_reached (); + } +} + +static void +fixture_finish (fixture_t *fixture, gconstpointer user_data) +{ + 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 (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 (b, ufuncs); + hb_unicode_funcs_destroy (ufuncs); + g_assert (hb_buffer_get_unicode_funcs (b) == ufuncs); + + hb_buffer_set_direction (b, HB_DIRECTION_RTL); + g_assert (hb_buffer_get_direction (b) == HB_DIRECTION_RTL); + + hb_buffer_set_script (b, HB_SCRIPT_ARABIC); + g_assert (hb_buffer_get_script (b) == HB_SCRIPT_ARABIC); + + hb_buffer_set_language (b, hb_language_from_string ("fa", -1)); + g_assert (hb_buffer_get_language (b) == hb_language_from_string ("Fa", -1)); + + + /* test reset clears properties */ + + hb_buffer_reset (b); + + 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 (b), ==, 0); + return; + } + + 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); + + for (i = 0; i < len; i++) { + g_assert_cmphex (glyphs[i].mask, ==, 1); + g_assert_cmphex (glyphs[i].var1.u32, ==, 0); + g_assert_cmphex (glyphs[i].var2.u32, ==, 0); + } + + for (i = 0; i < len; i++) { + unsigned int cluster; + cluster = 1+i; + if (i >= 2) { + if (buffer_type == BUFFER_UTF16) + cluster++; + else if (buffer_type == BUFFER_UTF8) + cluster += 3; + } + g_assert_cmphex (glyphs[i].codepoint, ==, utf32[1+i]); + g_assert_cmphex (glyphs[i].cluster, ==, cluster); + } + + /* reverse, test, and reverse back */ + + hb_buffer_reverse (b); + for (i = 0; i < len; i++) + g_assert_cmphex (glyphs[i].codepoint, ==, utf32[len-i]); + + 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 (b); + for (i = 0; i < len; i++) + g_assert_cmphex (glyphs[i].codepoint, ==, utf32[len-i]); + + hb_buffer_reverse_clusters (b); + for (i = 0; i < len; i++) + g_assert_cmphex (glyphs[i].codepoint, ==, utf32[1+i]); + + /* now form a cluster and test again */ + glyphs[2].cluster = glyphs[1].cluster; + + /* reverse, test, and reverse back */ + + hb_buffer_reverse (b); + for (i = 0; i < len; i++) + g_assert_cmphex (glyphs[i].codepoint, ==, utf32[len-i]); + + 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 (b); + for (i = 0; i < len; i++) { + unsigned int j = len-1-i; + if (j == 1) + j = 2; + else if (j == 2) + j = 1; + g_assert_cmphex (glyphs[i].codepoint, ==, utf32[1+j]); + } + + hb_buffer_reverse_clusters (b); + for (i = 0; i < len; i++) + g_assert_cmphex (glyphs[i].codepoint, ==, utf32[1+i]); + + + /* test setting length */ + + /* enlarge */ + 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 (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 (b)); + + + /* test reset clears content */ + + 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 (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); + g_assert_cmpint (0, ==, positions[i].y_advance); + g_assert_cmpint (0, ==, positions[i].x_offset); + g_assert_cmpint (0, ==, positions[i].y_offset); + g_assert_cmpint (0, ==, positions[i].var.i32); + } + + /* test reset clears content */ + hb_buffer_reset (b); + g_assert_cmpint (hb_buffer_get_length (b), ==, 0); +} + +static void +test_buffer_allocation (fixture_t *fixture, gconstpointer user_data) +{ + hb_buffer_t *b = fixture->buffer; + + 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 (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 (b, 50)); + g_assert_cmpint (hb_buffer_get_length (b), ==, 0); + g_assert (!hb_buffer_allocation_successful (b)); + + hb_buffer_reset (b); + g_assert (hb_buffer_allocation_successful (b)); + + /* all allocation and size */ + g_assert (!hb_buffer_pre_allocate (b, ((unsigned int) -1) / 20 + 1)); + g_assert (!hb_buffer_allocation_successful (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 (b, ((unsigned int) -1) / 20 - 1)); + g_assert (!hb_buffer_allocation_successful (b)); + } + + hb_buffer_reset (b); + g_assert (hb_buffer_allocation_successful (b)); +} + + +typedef struct { + const char utf8[8]; + const uint32_t codepoints[8]; +} utf8_conversion_test_t; + +/* note: we skip the first and last byte when adding to buffer */ +static const utf8_conversion_test_t utf8_conversion_tests[] = { + {"a\303\207", {-1}}, + {"a\303\207b", {0xC7}}, + {"ab\303cd", {'b', -1, 'c'}}, + {"ab\303\302\301cd", {'b', -1, -1, -1, 'c'}} +}; + +static void +test_buffer_utf8_conversion (void) +{ + hb_buffer_t *b; + hb_glyph_info_t *glyphs; + unsigned int bytes, chars, i, j, len; + + b = hb_buffer_create (); + + for (i = 0; i < G_N_ELEMENTS (utf8_conversion_tests); i++) + { + const utf8_conversion_test_t *test = &utf8_conversion_tests[i]; + char *escaped; + + escaped = g_strescape (test->utf8, NULL); + g_test_message ("UTF-8 test #%d: %s", i, escaped); + g_free (escaped); + + bytes = strlen (test->utf8); + for (chars = 0; test->codepoints[chars]; chars++) + ; + + hb_buffer_reset (b); + hb_buffer_add_utf8 (b, test->utf8, bytes, 1, bytes - 2); + + glyphs = hb_buffer_get_glyph_infos (b, &len); + g_assert_cmpint (len, ==, chars); + for (j = 0; j < chars; j++) + g_assert_cmphex (glyphs[j].codepoint, ==, test->codepoints[j]); + } + + hb_buffer_destroy (b); +} + + + +/* Following test table is adapted from glib/glib/tests/utf8-validate.c + * with relicensing permission from Matthias Clasen. */ + +typedef struct { + const char *utf8; + int max_len; + unsigned int offset; + gboolean valid; +} utf8_validity_test_t; + +static const utf8_validity_test_t utf8_validity_tests[] = { + /* some tests to check max_len handling */ + /* length 1 */ + { "abcde", -1, 5, TRUE }, + { "abcde", 3, 3, TRUE }, + { "abcde", 5, 5, TRUE }, + /* length 2 */ + { "\xc2\xa9\xc2\xa9\xc2\xa9", -1, 6, TRUE }, + { "\xc2\xa9\xc2\xa9\xc2\xa9", 1, 0, FALSE }, + { "\xc2\xa9\xc2\xa9\xc2\xa9", 2, 2, TRUE }, + { "\xc2\xa9\xc2\xa9\xc2\xa9", 3, 2, FALSE }, + { "\xc2\xa9\xc2\xa9\xc2\xa9", 4, 4, TRUE }, + { "\xc2\xa9\xc2\xa9\xc2\xa9", 5, 4, FALSE }, + { "\xc2\xa9\xc2\xa9\xc2\xa9", 6, 6, TRUE }, + /* length 3 */ + { "\xe2\x89\xa0\xe2\x89\xa0", -1, 6, TRUE }, + { "\xe2\x89\xa0\xe2\x89\xa0", 1, 0, FALSE }, + { "\xe2\x89\xa0\xe2\x89\xa0", 2, 0, FALSE }, + { "\xe2\x89\xa0\xe2\x89\xa0", 3, 3, TRUE }, + { "\xe2\x89\xa0\xe2\x89\xa0", 4, 3, FALSE }, + { "\xe2\x89\xa0\xe2\x89\xa0", 5, 3, FALSE }, + { "\xe2\x89\xa0\xe2\x89\xa0", 6, 6, TRUE }, + + /* examples from http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt */ + /* greek 'kosme' */ + { "\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5", -1, 11, TRUE }, + /* first sequence of each length */ + { "\x00", -1, 0, TRUE }, + { "\xc2\x80", -1, 2, TRUE }, + { "\xe0\xa0\x80", -1, 3, TRUE }, + { "\xf0\x90\x80\x80", -1, 4, TRUE }, + { "\xf8\x88\x80\x80\x80", -1, 0, FALSE }, + { "\xfc\x84\x80\x80\x80\x80", -1, 0, FALSE }, + /* last sequence of each length */ + { "\x7f", -1, 1, TRUE }, + { "\xdf\xbf", -1, 2, TRUE }, + { "\xef\xbf\xbf", -1, 0, TRUE }, + { "\xf7\xbf\xbf\xbf", -1, 0, TRUE }, + { "\xfb\xbf\xbf\xbf\xbf", -1, 0, FALSE }, + { "\xfd\xbf\xbf\xbf\xbf\xbf", -1, 0, FALSE }, + /* other boundary conditions */ + { "\xed\x9f\xbf", -1, 3, TRUE }, + { "\xee\x80\x80", -1, 3, TRUE }, + { "\xef\xbf\xbd", -1, 3, TRUE }, + { "\xf4\x8f\xbf\xbf", -1, 0, TRUE }, + /* malformed sequences */ + /* continuation bytes */ + { "\x80", -1, 0, FALSE }, + { "\xbf", -1, 0, FALSE }, + { "\x80\xbf", -1, 0, FALSE }, + { "\x80\xbf\x80", -1, 0, FALSE }, + { "\x80\xbf\x80\xbf", -1, 0, FALSE }, + { "\x80\xbf\x80\xbf\x80", -1, 0, FALSE }, + { "\x80\xbf\x80\xbf\x80\xbf", -1, 0, FALSE }, + { "\x80\xbf\x80\xbf\x80\xbf\x80", -1, 0, FALSE }, + + /* all possible continuation byte */ + { "\x80", -1, 0, FALSE }, + { "\x81", -1, 0, FALSE }, + { "\x82", -1, 0, FALSE }, + { "\x83", -1, 0, FALSE }, + { "\x84", -1, 0, FALSE }, + { "\x85", -1, 0, FALSE }, + { "\x86", -1, 0, FALSE }, + { "\x87", -1, 0, FALSE }, + { "\x88", -1, 0, FALSE }, + { "\x89", -1, 0, FALSE }, + { "\x8a", -1, 0, FALSE }, + { "\x8b", -1, 0, FALSE }, + { "\x8c", -1, 0, FALSE }, + { "\x8d", -1, 0, FALSE }, + { "\x8e", -1, 0, FALSE }, + { "\x8f", -1, 0, FALSE }, + { "\x90", -1, 0, FALSE }, + { "\x91", -1, 0, FALSE }, + { "\x92", -1, 0, FALSE }, + { "\x93", -1, 0, FALSE }, + { "\x94", -1, 0, FALSE }, + { "\x95", -1, 0, FALSE }, + { "\x96", -1, 0, FALSE }, + { "\x97", -1, 0, FALSE }, + { "\x98", -1, 0, FALSE }, + { "\x99", -1, 0, FALSE }, + { "\x9a", -1, 0, FALSE }, + { "\x9b", -1, 0, FALSE }, + { "\x9c", -1, 0, FALSE }, + { "\x9d", -1, 0, FALSE }, + { "\x9e", -1, 0, FALSE }, + { "\x9f", -1, 0, FALSE }, + { "\xa0", -1, 0, FALSE }, + { "\xa1", -1, 0, FALSE }, + { "\xa2", -1, 0, FALSE }, + { "\xa3", -1, 0, FALSE }, + { "\xa4", -1, 0, FALSE }, + { "\xa5", -1, 0, FALSE }, + { "\xa6", -1, 0, FALSE }, + { "\xa7", -1, 0, FALSE }, + { "\xa8", -1, 0, FALSE }, + { "\xa9", -1, 0, FALSE }, + { "\xaa", -1, 0, FALSE }, + { "\xab", -1, 0, FALSE }, + { "\xac", -1, 0, FALSE }, + { "\xad", -1, 0, FALSE }, + { "\xae", -1, 0, FALSE }, + { "\xaf", -1, 0, FALSE }, + { "\xb0", -1, 0, FALSE }, + { "\xb1", -1, 0, FALSE }, + { "\xb2", -1, 0, FALSE }, + { "\xb3", -1, 0, FALSE }, + { "\xb4", -1, 0, FALSE }, + { "\xb5", -1, 0, FALSE }, + { "\xb6", -1, 0, FALSE }, + { "\xb7", -1, 0, FALSE }, + { "\xb8", -1, 0, FALSE }, + { "\xb9", -1, 0, FALSE }, + { "\xba", -1, 0, FALSE }, + { "\xbb", -1, 0, FALSE }, + { "\xbc", -1, 0, FALSE }, + { "\xbd", -1, 0, FALSE }, + { "\xbe", -1, 0, FALSE }, + { "\xbf", -1, 0, FALSE }, + /* lone start characters */ + { "\xc0\x20", -1, 0, FALSE }, + { "\xc1\x20", -1, 0, FALSE }, + { "\xc2\x20", -1, 0, FALSE }, + { "\xc3\x20", -1, 0, FALSE }, + { "\xc4\x20", -1, 0, FALSE }, + { "\xc5\x20", -1, 0, FALSE }, + { "\xc6\x20", -1, 0, FALSE }, + { "\xc7\x20", -1, 0, FALSE }, + { "\xc8\x20", -1, 0, FALSE }, + { "\xc9\x20", -1, 0, FALSE }, + { "\xca\x20", -1, 0, FALSE }, + { "\xcb\x20", -1, 0, FALSE }, + { "\xcc\x20", -1, 0, FALSE }, + { "\xcd\x20", -1, 0, FALSE }, + { "\xce\x20", -1, 0, FALSE }, + { "\xcf\x20", -1, 0, FALSE }, + { "\xd0\x20", -1, 0, FALSE }, + { "\xd1\x20", -1, 0, FALSE }, + { "\xd2\x20", -1, 0, FALSE }, + { "\xd3\x20", -1, 0, FALSE }, + { "\xd4\x20", -1, 0, FALSE }, + { "\xd5\x20", -1, 0, FALSE }, + { "\xd6\x20", -1, 0, FALSE }, + { "\xd7\x20", -1, 0, FALSE }, + { "\xd8\x20", -1, 0, FALSE }, + { "\xd9\x20", -1, 0, FALSE }, + { "\xda\x20", -1, 0, FALSE }, + { "\xdb\x20", -1, 0, FALSE }, + { "\xdc\x20", -1, 0, FALSE }, + { "\xdd\x20", -1, 0, FALSE }, + { "\xde\x20", -1, 0, FALSE }, + { "\xdf\x20", -1, 0, FALSE }, + { "\xe0\x20", -1, 0, FALSE }, + { "\xe1\x20", -1, 0, FALSE }, + { "\xe2\x20", -1, 0, FALSE }, + { "\xe3\x20", -1, 0, FALSE }, + { "\xe4\x20", -1, 0, FALSE }, + { "\xe5\x20", -1, 0, FALSE }, + { "\xe6\x20", -1, 0, FALSE }, + { "\xe7\x20", -1, 0, FALSE }, + { "\xe8\x20", -1, 0, FALSE }, + { "\xe9\x20", -1, 0, FALSE }, + { "\xea\x20", -1, 0, FALSE }, + { "\xeb\x20", -1, 0, FALSE }, + { "\xec\x20", -1, 0, FALSE }, + { "\xed\x20", -1, 0, FALSE }, + { "\xee\x20", -1, 0, FALSE }, + { "\xef\x20", -1, 0, FALSE }, + { "\xf0\x20", -1, 0, FALSE }, + { "\xf1\x20", -1, 0, FALSE }, + { "\xf2\x20", -1, 0, FALSE }, + { "\xf3\x20", -1, 0, FALSE }, + { "\xf4\x20", -1, 0, FALSE }, + { "\xf5\x20", -1, 0, FALSE }, + { "\xf6\x20", -1, 0, FALSE }, + { "\xf7\x20", -1, 0, FALSE }, + { "\xf8\x20", -1, 0, FALSE }, + { "\xf9\x20", -1, 0, FALSE }, + { "\xfa\x20", -1, 0, FALSE }, + { "\xfb\x20", -1, 0, FALSE }, + { "\xfc\x20", -1, 0, FALSE }, + { "\xfd\x20", -1, 0, FALSE }, + /* missing continuation bytes */ + { "\x20\xc0", -1, 1, FALSE }, + { "\x20\xe0\x80", -1, 1, FALSE }, + { "\x20\xf0\x80\x80", -1, 1, FALSE }, + { "\x20\xf8\x80\x80\x80", -1, 1, FALSE }, + { "\x20\xfc\x80\x80\x80\x80", -1, 1, FALSE }, + { "\x20\xdf", -1, 1, FALSE }, + { "\x20\xef\xbf", -1, 1, FALSE }, + { "\x20\xf7\xbf\xbf", -1, 1, FALSE }, + { "\x20\xfb\xbf\xbf\xbf", -1, 1, FALSE }, + { "\x20\xfd\xbf\xbf\xbf\xbf", -1, 1, FALSE }, + /* impossible bytes */ + { "\x20\xfe\x20", -1, 1, FALSE }, + { "\x20\xff\x20", -1, 1, FALSE }, +#if 0 + /* XXX fix these, or document that we don't detect them? */ + /* overlong sequences */ + { "\x20\xc0\xaf\x20", -1, 1, FALSE }, + { "\x20\xe0\x80\xaf\x20", -1, 1, FALSE }, + { "\x20\xf0\x80\x80\xaf\x20", -1, 1, FALSE }, + { "\x20\xf8\x80\x80\x80\xaf\x20", -1, 1, FALSE }, + { "\x20\xfc\x80\x80\x80\x80\xaf\x20", -1, 1, FALSE }, + { "\x20\xc1\xbf\x20", -1, 1, FALSE }, + { "\x20\xe0\x9f\xbf\x20", -1, 1, FALSE }, + { "\x20\xf0\x8f\xbf\xbf\x20", -1, 1, FALSE }, + { "\x20\xf8\x87\xbf\xbf\xbf\x20", -1, 1, FALSE }, + { "\x20\xfc\x83\xbf\xbf\xbf\xbf\x20", -1, 1, FALSE }, + { "\x20\xc0\x80\x20", -1, 1, FALSE }, + { "\x20\xe0\x80\x80\x20", -1, 1, FALSE }, + { "\x20\xf0\x80\x80\x80\x20", -1, 1, FALSE }, + { "\x20\xf8\x80\x80\x80\x80\x20", -1, 1, FALSE }, + { "\x20\xfc\x80\x80\x80\x80\x80\x20", -1, 1, FALSE }, + /* illegal code positions */ + { "\x20\xed\xa0\x80\x20", -1, 1, FALSE }, + { "\x20\xed\xad\xbf\x20", -1, 1, FALSE }, + { "\x20\xed\xae\x80\x20", -1, 1, FALSE }, + { "\x20\xed\xaf\xbf\x20", -1, 1, FALSE }, + { "\x20\xed\xb0\x80\x20", -1, 1, FALSE }, + { "\x20\xed\xbe\x80\x20", -1, 1, FALSE }, + { "\x20\xed\xbf\xbf\x20", -1, 1, FALSE }, + { "\x20\xed\xa0\x80\xed\xb0\x80\x20", -1, 1, FALSE }, + { "\x20\xed\xa0\x80\xed\xbf\xbf\x20", -1, 1, FALSE }, + { "\x20\xed\xad\xbf\xed\xb0\x80\x20", -1, 1, FALSE }, + { "\x20\xed\xad\xbf\xed\xbf\xbf\x20", -1, 1, FALSE }, + { "\x20\xed\xae\x80\xed\xb0\x80\x20", -1, 1, FALSE }, + { "\x20\xed\xae\x80\xed\xbf\xbf\x20", -1, 1, FALSE }, + { "\x20\xed\xaf\xbf\xed\xb0\x80\x20", -1, 1, FALSE }, + { "\x20\xed\xaf\xbf\xed\xbf\xbf\x20", -1, 1, FALSE }, + { "\x20\xef\xbf\xbe\x20", -1, 1, FALSE }, + { "\x20\xef\xbf\xbf\x20", -1, 1, FALSE }, +#endif + { "", -1, 0, TRUE } +}; + +static void +test_buffer_utf8_validity (void) +{ + hb_buffer_t *b; + unsigned int i; + + b = hb_buffer_create (); + + for (i = 0; i < G_N_ELEMENTS (utf8_validity_tests); i++) + { + const utf8_validity_test_t *test = &utf8_validity_tests[i]; + unsigned int text_bytes, segment_bytes, j, len; + hb_glyph_info_t *glyphs; + char *escaped; + + escaped = g_strescape (test->utf8, NULL); + g_test_message ("UTF-8 test #%d: %s", i, escaped); + g_free (escaped); + + text_bytes = strlen (test->utf8); + if (test->max_len == -1) + segment_bytes = text_bytes; + else + segment_bytes = test->max_len; + + hb_buffer_reset (b); + hb_buffer_add_utf8 (b, test->utf8, text_bytes, 0, segment_bytes); + + glyphs = hb_buffer_get_glyph_infos (b, &len); + for (j = 0; j < len; j++) + if (glyphs[j].codepoint == (hb_codepoint_t) -1) + break; + + g_assert (test->valid ? j == len : j < len); + if (!test->valid) + g_assert (glyphs[j].cluster == test->offset); + } + + hb_buffer_destroy (b); +} + + +typedef struct { + const uint16_t utf16[8]; + const uint32_t codepoints[8]; +} utf16_conversion_test_t; + +/* note: we skip the first and last item from utf16 when adding to buffer */ +static const utf16_conversion_test_t utf16_conversion_tests[] = { + {{0x41, 0x004D, 0x0430, 0x4E8C, 0xD800, 0xDF02, 0x61} , {0x004D, 0x0430, 0x4E8C, 0x10302}}, + {{0x41, 0xD800, 0xDF02, 0x61}, {0x10302}}, + {{0x41, 0xD800, 0xDF02}, {-1}}, + {{0x41, 0x61, 0xD800, 0xDF02}, {0x61, -1}}, + {{0x41, 0xD800, 0x61, 0xDF02}, {-1, 0x61}}, + {{0x41, 0x61}, {}} +}; + +static void +test_buffer_utf16_conversion (void) +{ + hb_buffer_t *b; + unsigned int i; + + b = hb_buffer_create (); + + for (i = 0; i < G_N_ELEMENTS (utf16_conversion_tests); i++) + { + const utf16_conversion_test_t *test = &utf16_conversion_tests[i]; + unsigned int u_len, chars, j, len; + hb_glyph_info_t *glyphs; + + g_test_message ("UTF-16 test #%d", i); + + for (u_len = 0; test->utf16[u_len]; u_len++) + ; + for (chars = 0; test->codepoints[chars]; chars++) + ; + + hb_buffer_reset (b); + hb_buffer_add_utf16 (b, test->utf16, u_len, 1, u_len - 2); + + glyphs = hb_buffer_get_glyph_infos (b, &len); + g_assert_cmpint (len, ==, chars); + for (j = 0; j < chars; j++) + g_assert_cmphex (glyphs[j].codepoint, ==, test->codepoints[j]); + } + + hb_buffer_destroy (b); +} + +static void +test_empty (hb_buffer_t *b) +{ + g_assert_cmpint (hb_buffer_get_length (b), ==, 0); + g_assert (!hb_buffer_get_glyph_infos (b, NULL)); + g_assert (!hb_buffer_get_glyph_positions (b, NULL)); +} + +static void +test_buffer_empty (void) +{ + hb_buffer_t *b = hb_buffer_get_empty (); + + g_assert (hb_buffer_get_empty ()); + g_assert (hb_buffer_get_empty () == b); + + g_assert (!hb_buffer_allocation_successful (b)); + + test_empty (b); + + hb_buffer_add_utf32 (b, utf32, G_N_ELEMENTS (utf32), 1, G_N_ELEMENTS (utf32) - 2); + + test_empty (b); + + hb_buffer_reverse (b); + hb_buffer_reverse_clusters (b); + + g_assert (!hb_buffer_set_length (b, 10)); + + test_empty (b); + + g_assert (hb_buffer_set_length (b, 0)); + + test_empty (b); + + g_assert (!hb_buffer_allocation_successful (b)); + + hb_buffer_reset (b); + + test_empty (b); + + g_assert (!hb_buffer_allocation_successful (b)); +} + +int +main (int argc, char **argv) +{ + unsigned int i; + + hb_test_init (&argc, &argv); + + for (i = 0; i < BUFFER_NUM_TYPES; i++) + { + const void *buffer_type = GINT_TO_POINTER (i); + const char *buffer_name = buffer_names[i]; + + hb_test_add_fixture_flavor (fixture, buffer_type, buffer_name, test_buffer_properties); + hb_test_add_fixture_flavor (fixture, buffer_type, buffer_name, test_buffer_contents); + hb_test_add_fixture_flavor (fixture, buffer_type, buffer_name, test_buffer_positions); + } + + hb_test_add_fixture (fixture, GINT_TO_POINTER (BUFFER_EMPTY), test_buffer_allocation); + + hb_test_add (test_buffer_utf8_conversion); + hb_test_add (test_buffer_utf8_validity); + hb_test_add (test_buffer_utf16_conversion); + hb_test_add (test_buffer_empty); + + return hb_test_run(); +}
diff --git a/test/api/test-c.c b/test/api/test-c.c new file mode 100644 index 0000000..25a38e5 --- /dev/null +++ b/test/api/test-c.c
@@ -0,0 +1,58 @@ +/* + * 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 + */ + +/* This file tests that all headers can be included from .c files */ + + +#include <config.h> + +#include <hb.h> + +#ifdef HAVE_GLIB +#include <hb-glib.h> +#endif + +#ifdef HAVE_ICU +#include <hb-icu.h> +#endif + +#ifdef HAVE_FREETYPE +#include <hb-ft.h> +#endif + +#ifdef HAVE_OT +#include <hb-ot.h> +#endif + +#ifdef HAVE_UNISCRIBE +#include <hb-uniscribe.h> +#endif + +int +main (int argc, char **argv) +{ + return !*hb_shape_list_shapers (); +}
diff --git a/test/api/test-common.c b/test/api/test-common.c new file mode 100644 index 0000000..e00e601 --- /dev/null +++ b/test/api/test-common.c
@@ -0,0 +1,213 @@ +/* + * 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-common.h */ + + +static void +test_types_int (void) +{ + /* We already ASSERT_STATIC these in hb-private.h, but anyway */ + g_assert_cmpint (sizeof (int8_t), ==, 1); + g_assert_cmpint (sizeof (uint8_t), ==, 1); + g_assert_cmpint (sizeof (int16_t), ==, 2); + g_assert_cmpint (sizeof (uint16_t), ==, 2); + g_assert_cmpint (sizeof (int32_t), ==, 4); + g_assert_cmpint (sizeof (uint32_t), ==, 4); + g_assert_cmpint (sizeof (int64_t), ==, 8); + g_assert_cmpint (sizeof (uint64_t), ==, 8); + + g_assert_cmpint (sizeof (hb_codepoint_t), ==, 4); + g_assert_cmpint (sizeof (hb_position_t), ==, 4); + g_assert_cmpint (sizeof (hb_mask_t), ==, 4); + g_assert_cmpint (sizeof (hb_var_int_t), ==, 4); +} + +static void +test_types_direction (void) +{ + g_assert_cmpint ((signed) HB_DIRECTION_INVALID, ==, -1); + g_assert_cmpint (HB_DIRECTION_LTR, ==, 0); + + g_assert (HB_DIRECTION_IS_HORIZONTAL (HB_DIRECTION_LTR)); + g_assert (HB_DIRECTION_IS_HORIZONTAL (HB_DIRECTION_RTL)); + g_assert (!HB_DIRECTION_IS_HORIZONTAL (HB_DIRECTION_TTB)); + g_assert (!HB_DIRECTION_IS_HORIZONTAL (HB_DIRECTION_BTT)); + + g_assert (!HB_DIRECTION_IS_VERTICAL (HB_DIRECTION_LTR)); + g_assert (!HB_DIRECTION_IS_VERTICAL (HB_DIRECTION_RTL)); + g_assert (HB_DIRECTION_IS_VERTICAL (HB_DIRECTION_TTB)); + g_assert (HB_DIRECTION_IS_VERTICAL (HB_DIRECTION_BTT)); + + g_assert (HB_DIRECTION_IS_FORWARD (HB_DIRECTION_LTR)); + g_assert (HB_DIRECTION_IS_FORWARD (HB_DIRECTION_TTB)); + g_assert (!HB_DIRECTION_IS_FORWARD (HB_DIRECTION_RTL)); + g_assert (!HB_DIRECTION_IS_FORWARD (HB_DIRECTION_BTT)); + + g_assert (!HB_DIRECTION_IS_BACKWARD (HB_DIRECTION_LTR)); + g_assert (!HB_DIRECTION_IS_BACKWARD (HB_DIRECTION_TTB)); + g_assert (HB_DIRECTION_IS_BACKWARD (HB_DIRECTION_RTL)); + g_assert (HB_DIRECTION_IS_BACKWARD (HB_DIRECTION_BTT)); + + g_assert_cmpint (HB_DIRECTION_REVERSE (HB_DIRECTION_LTR), ==, HB_DIRECTION_RTL); + g_assert_cmpint (HB_DIRECTION_REVERSE (HB_DIRECTION_RTL), ==, HB_DIRECTION_LTR); + g_assert_cmpint (HB_DIRECTION_REVERSE (HB_DIRECTION_TTB), ==, HB_DIRECTION_BTT); + g_assert_cmpint (HB_DIRECTION_REVERSE (HB_DIRECTION_BTT), ==, HB_DIRECTION_TTB); + + g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string (NULL, -1)); + g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string ("", -1)); + g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string ("t", 0)); + g_assert_cmpint (HB_DIRECTION_INVALID, ==, hb_direction_from_string ("x", -1)); + g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("r", -1)); + g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("rtl", -1)); + g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("RtL", -1)); + g_assert_cmpint (HB_DIRECTION_RTL, ==, hb_direction_from_string ("right-to-left", -1)); + g_assert_cmpint (HB_DIRECTION_TTB, ==, hb_direction_from_string ("ttb", -1)); + + g_assert (0 == strcmp ("ltr", hb_direction_to_string (HB_DIRECTION_LTR))); + g_assert (0 == strcmp ("rtl", hb_direction_to_string (HB_DIRECTION_RTL))); + g_assert (0 == strcmp ("ttb", hb_direction_to_string (HB_DIRECTION_TTB))); + g_assert (0 == strcmp ("btt", hb_direction_to_string (HB_DIRECTION_BTT))); + g_assert (0 == strcmp ("invalid", hb_direction_to_string (HB_DIRECTION_INVALID))); +} + +static void +test_types_tag (void) +{ + g_assert_cmphex (HB_TAG_NONE, ==, 0); + + g_assert_cmphex (HB_TAG ('a','B','c','D'), ==, 0x61426344); + + g_assert_cmphex (hb_tag_from_string ("aBcDe", -1), ==, 0x61426344); + g_assert_cmphex (hb_tag_from_string ("aBcD", -1), ==, 0x61426344); + g_assert_cmphex (hb_tag_from_string ("aBc", -1), ==, 0x61426320); + g_assert_cmphex (hb_tag_from_string ("aB", -1), ==, 0x61422020); + g_assert_cmphex (hb_tag_from_string ("a", -1), ==, 0x61202020); + g_assert_cmphex (hb_tag_from_string ("aBcDe", 1), ==, 0x61202020); + g_assert_cmphex (hb_tag_from_string ("aBcDe", 2), ==, 0x61422020); + g_assert_cmphex (hb_tag_from_string ("aBcDe", 3), ==, 0x61426320); + g_assert_cmphex (hb_tag_from_string ("aBcDe", 4), ==, 0x61426344); + g_assert_cmphex (hb_tag_from_string ("aBcDe", 4), ==, 0x61426344); + + g_assert_cmphex (hb_tag_from_string ("", -1), ==, HB_TAG_NONE); + g_assert_cmphex (hb_tag_from_string ("x", 0), ==, HB_TAG_NONE); + g_assert_cmphex (hb_tag_from_string (NULL, -1), ==, HB_TAG_NONE); +} + +static void +test_types_script (void) +{ + hb_tag_t arab = HB_TAG_CHAR4 ("arab"); + hb_tag_t Arab = HB_TAG_CHAR4 ("Arab"); + hb_tag_t ARAB = HB_TAG_CHAR4 ("ARAB"); + + hb_tag_t wWyZ = HB_TAG_CHAR4 ("wWyZ"); + hb_tag_t Wwyz = HB_TAG_CHAR4 ("Wwyz"); + + hb_tag_t x123 = HB_TAG_CHAR4 ("x123"); + + g_assert_cmpint (HB_SCRIPT_INVALID, ==, (hb_script_t) HB_TAG_NONE); + g_assert_cmphex (HB_SCRIPT_ARABIC, !=, HB_SCRIPT_LATIN); + + g_assert_cmphex (HB_SCRIPT_INVALID, ==, hb_script_from_string (NULL, -1)); + g_assert_cmphex (HB_SCRIPT_INVALID, ==, hb_script_from_string ("", -1)); + g_assert_cmphex (HB_SCRIPT_INVALID, ==, hb_script_from_string ("x", 0)); + g_assert_cmphex (HB_SCRIPT_UNKNOWN, ==, hb_script_from_string ("x", -1)); + + g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("arab", -1)); + g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("Arab", -1)); + g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("ARAB", -1)); + g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_string ("Arabic", 6)); + g_assert_cmphex (HB_SCRIPT_ARABIC, !=, hb_script_from_string ("Arabic", 3)); + + g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_iso15924_tag (arab)); + g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_iso15924_tag (Arab)); + g_assert_cmphex (HB_SCRIPT_ARABIC, ==, hb_script_from_iso15924_tag (ARAB)); + + /* Arbitrary tags that look like may be valid ISO 15924 should be preserved. */ + g_assert_cmphex (HB_SCRIPT_UNKNOWN, !=, hb_script_from_string ("wWyZ", -1)); + g_assert_cmphex (HB_SCRIPT_UNKNOWN, !=, hb_script_from_iso15924_tag (wWyZ)); + /* Otherwise, UNKNOWN should be returned. */ + g_assert_cmphex (HB_SCRIPT_UNKNOWN, ==, hb_script_from_string ("x123", -1)); + g_assert_cmphex (HB_SCRIPT_UNKNOWN, ==, hb_script_from_iso15924_tag (x123)); + + g_assert_cmphex (hb_script_to_iso15924_tag (HB_SCRIPT_ARABIC), ==, Arab); + g_assert_cmphex (hb_script_to_iso15924_tag (hb_script_from_iso15924_tag (wWyZ)), ==, Wwyz); + + g_assert_cmpint (hb_script_get_horizontal_direction (HB_SCRIPT_LATIN), ==, HB_DIRECTION_LTR); + g_assert_cmpint (hb_script_get_horizontal_direction (HB_SCRIPT_ARABIC), ==, HB_DIRECTION_RTL); + g_assert_cmpint (hb_script_get_horizontal_direction (hb_script_from_iso15924_tag (wWyZ)), ==, HB_DIRECTION_LTR); +} + +static void +test_types_language (void) +{ + hb_language_t fa = hb_language_from_string ("fa", -1); + hb_language_t fa_IR = hb_language_from_string ("fa_IR", -1); + hb_language_t fa_ir = hb_language_from_string ("fa-ir", -1); + hb_language_t en = hb_language_from_string ("en", -1); + + g_assert (HB_LANGUAGE_INVALID == NULL); + + g_assert (fa != NULL); + g_assert (fa_IR != NULL); + g_assert (fa_IR == fa_ir); + + g_assert (en != NULL); + g_assert (en != fa); + + /* Test recall */ + g_assert (en == hb_language_from_string ("en", -1)); + g_assert (en == hb_language_from_string ("eN", -1)); + g_assert (en == hb_language_from_string ("Enx", 2)); + + g_assert (HB_LANGUAGE_INVALID == hb_language_from_string (NULL, -1)); + g_assert (HB_LANGUAGE_INVALID == hb_language_from_string ("", -1)); + g_assert (HB_LANGUAGE_INVALID == hb_language_from_string ("en", 0)); + g_assert (HB_LANGUAGE_INVALID != hb_language_from_string ("en", 1)); + g_assert (NULL == hb_language_to_string (HB_LANGUAGE_INVALID)); + + /* Not sure how to test this better. Setting env vars + * here doesn't sound like the right approach, and I'm + * not sure that it even works. */ + g_assert (HB_LANGUAGE_INVALID != hb_language_get_default ()); +} + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_types_int); + hb_test_add (test_types_direction); + hb_test_add (test_types_tag); + hb_test_add (test_types_script); + hb_test_add (test_types_language); + + return hb_test_run(); +}
diff --git a/test/api/test-cplusplus.cc b/test/api/test-cplusplus.cc new file mode 100644 index 0000000..3313d74 --- /dev/null +++ b/test/api/test-cplusplus.cc
@@ -0,0 +1,30 @@ +/* + * 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 + */ + +/* This file tests that all headers can be included from .cc files */ + + +#include "test-c.c"
diff --git a/test/api/test-font.c b/test/api/test-font.c new file mode 100644 index 0000000..40540c4 --- /dev/null +++ b/test/api/test-font.c
@@ -0,0 +1,502 @@ +/* + * 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-font.h */ + + +static const char test_data[] = "test\0data"; + + +static void +test_face_empty (void) +{ + g_assert (hb_face_get_empty ()); + g_assert (hb_face_get_empty () == hb_face_create (hb_blob_get_empty (), 0)); + g_assert (hb_face_get_empty () == hb_face_create (NULL, 0)); + + g_assert (hb_face_reference_table (hb_face_get_empty (), HB_TAG ('h','e','a','d')) == hb_blob_get_empty ()); + + g_assert_cmpint (hb_face_get_upem (hb_face_get_empty ()), ==, 1000); +} + +static void +test_face_create (void) +{ + hb_face_t *face; + hb_blob_t *blob; + + blob = hb_blob_create (test_data, sizeof (test_data), HB_MEMORY_MODE_READONLY, NULL, NULL); + face = hb_face_create (blob, 0); + hb_blob_destroy (blob); + + g_assert (hb_face_reference_table (face, HB_TAG ('h','e','a','d')) == hb_blob_get_empty ()); + + g_assert_cmpint (hb_face_get_upem (face), ==, 1000); + + hb_face_destroy (face); +} + + +static void +free_up (void *user_data) +{ + int *freed = (int *) user_data; + + g_assert (!*freed); + + (*freed)++; +} + +static hb_blob_t * +get_table (hb_face_t *face, hb_tag_t tag, void *user_data) +{ + if (tag == HB_TAG ('a','b','c','d')) + return hb_blob_create (test_data, sizeof (test_data), HB_MEMORY_MODE_READONLY, NULL, NULL); + + return hb_blob_get_empty (); +} + +static void +test_face_createfortables (void) +{ + hb_face_t *face; + hb_blob_t *blob; + const char *data; + unsigned int len; + int freed = 0; + + face = hb_face_create_for_tables (get_table, &freed, free_up); + g_assert (!freed); + + g_assert (hb_face_reference_table (face, HB_TAG ('h','e','a','d')) == hb_blob_get_empty ()); + + blob = hb_face_reference_table (face, HB_TAG ('a','b','c','d')); + g_assert (blob != hb_blob_get_empty ()); + + data = hb_blob_get_data (blob, &len); + g_assert_cmpint (len, ==, sizeof (test_data)); + g_assert (0 == memcmp (data, test_data, sizeof (test_data))); + hb_blob_destroy (blob); + + g_assert_cmpint (hb_face_get_upem (face), ==, 1000); + + hb_face_destroy (face); + g_assert (freed); +} + +static void +_test_font_nil_funcs (hb_font_t *font) +{ + hb_codepoint_t glyph; + hb_position_t x, y; + hb_glyph_extents_t extents; + + x = y = 13; + g_assert (!hb_font_get_glyph_contour_point (font, 17, 2, &x, &y)); + g_assert_cmpint (x, ==, 0); + g_assert_cmpint (y, ==, 0); + + x = hb_font_get_glyph_h_advance (font, 17); + g_assert_cmpint (x, ==, 0); + + extents.x_bearing = extents.y_bearing = 13; + extents.width = extents.height = 15; + hb_font_get_glyph_extents (font, 17, &extents); + g_assert_cmpint (extents.x_bearing, ==, 0); + g_assert_cmpint (extents.y_bearing, ==, 0); + g_assert_cmpint (extents.width, ==, 0); + g_assert_cmpint (extents.height, ==, 0); + + glyph = 3; + g_assert (!hb_font_get_glyph (font, 17, 2, &glyph)); + g_assert_cmpint (glyph, ==, 0); + + x = 13; + x = hb_font_get_glyph_h_kerning (font, 17, 19); + g_assert_cmpint (x, ==, 0); +} + +static void +_test_fontfuncs_nil (hb_font_funcs_t *ffuncs) +{ + hb_blob_t *blob; + hb_face_t *face; + hb_font_t *font; + hb_font_t *subfont; + int freed = 0; + + blob = hb_blob_create (test_data, sizeof (test_data), HB_MEMORY_MODE_READONLY, NULL, NULL); + face = hb_face_create (blob, 0); + hb_blob_destroy (blob); + g_assert (!hb_face_is_immutable (face)); + font = hb_font_create (face); + g_assert (font); + g_assert (hb_face_is_immutable (face)); + hb_face_destroy (face); + + + hb_font_set_funcs (font, ffuncs, &freed, free_up); + g_assert_cmpint (freed, ==, 0); + + _test_font_nil_funcs (font); + + subfont = hb_font_create_sub_font (font); + g_assert (subfont); + + g_assert_cmpint (freed, ==, 0); + hb_font_destroy (font); + g_assert_cmpint (freed, ==, 0); + + _test_font_nil_funcs (subfont); + + hb_font_destroy (subfont); + g_assert_cmpint (freed, ==, 1); +} + +static void +test_fontfuncs_empty (void) +{ + g_assert (hb_font_funcs_get_empty ()); + g_assert (hb_font_funcs_is_immutable (hb_font_funcs_get_empty ())); + _test_fontfuncs_nil (hb_font_funcs_get_empty ()); +} + +static void +test_fontfuncs_nil (void) +{ + hb_font_funcs_t *ffuncs; + + ffuncs = hb_font_funcs_create (); + + g_assert (!hb_font_funcs_is_immutable (ffuncs)); + _test_fontfuncs_nil (hb_font_funcs_get_empty ()); + + hb_font_funcs_destroy (ffuncs); +} + +static hb_bool_t +contour_point_func1 (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y, + void *user_data) +{ + if (glyph == 1) { + *x = 2; + *y = 3; + return TRUE; + } + if (glyph == 2) { + *x = 4; + *y = 5; + return TRUE; + } + + return FALSE; +} + +static hb_bool_t +contour_point_func2 (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y, + void *user_data) +{ + if (glyph == 1) { + *x = 6; + *y = 7; + return TRUE; + } + + return hb_font_get_glyph_contour_point (hb_font_get_parent (font), + glyph, point_index, x, y); +} + +static hb_position_t +glyph_h_advance_func1 (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + void *user_data) +{ + if (glyph == 1) + return 8; + + return 0; +} + +static void +test_fontfuncs_subclassing (void) +{ + hb_blob_t *blob; + hb_face_t *face; + + hb_font_funcs_t *ffuncs1; + hb_font_funcs_t *ffuncs2; + + hb_font_t *font1; + hb_font_t *font2; + hb_font_t *font3; + + hb_position_t x; + hb_position_t y; + + blob = hb_blob_create (test_data, sizeof (test_data), HB_MEMORY_MODE_READONLY, NULL, NULL); + face = hb_face_create (blob, 0); + hb_blob_destroy (blob); + font1 = hb_font_create (face); + hb_face_destroy (face); + hb_font_set_scale (font1, 10, 10); + + /* setup font1 */ + ffuncs1 = hb_font_funcs_create (); + hb_font_funcs_set_glyph_contour_point_func (ffuncs1, contour_point_func1, NULL, NULL); + hb_font_funcs_set_glyph_h_advance_func (ffuncs1, glyph_h_advance_func1, NULL, NULL); + hb_font_set_funcs (font1, ffuncs1, NULL, NULL); + hb_font_funcs_destroy (ffuncs1); + + x = y = 1; + g_assert (hb_font_get_glyph_contour_point_for_origin (font1, 1, 2, HB_DIRECTION_LTR, &x, &y)); + g_assert_cmpint (x, ==, 2); + g_assert_cmpint (y, ==, 3); + g_assert (hb_font_get_glyph_contour_point_for_origin (font1, 2, 5, HB_DIRECTION_LTR, &x, &y)); + g_assert_cmpint (x, ==, 4); + g_assert_cmpint (y, ==, 5); + g_assert (!hb_font_get_glyph_contour_point_for_origin (font1, 3, 7, HB_DIRECTION_RTL, &x, &y)); + g_assert_cmpint (x, ==, 0); + g_assert_cmpint (y, ==, 0); + x = hb_font_get_glyph_h_advance (font1, 1); + g_assert_cmpint (x, ==, 8); + x = hb_font_get_glyph_h_advance (font1, 2); + g_assert_cmpint (x, ==, 0); + + + font2 = hb_font_create_sub_font (font1); + g_assert (hb_font_is_immutable (font1)); + hb_font_destroy (font1); + + /* setup font2 to override some funcs */ + ffuncs2 = hb_font_funcs_create (); + hb_font_funcs_set_glyph_contour_point_func (ffuncs2, contour_point_func2, NULL, NULL); + hb_font_set_funcs (font2, ffuncs2, NULL, NULL); + hb_font_funcs_destroy (ffuncs2); + + x = y = 1; + g_assert (hb_font_get_glyph_contour_point_for_origin (font2, 1, 2, HB_DIRECTION_LTR, &x, &y)); + g_assert_cmpint (x, ==, 6); + g_assert_cmpint (y, ==, 7); + g_assert (hb_font_get_glyph_contour_point_for_origin (font2, 2, 5, HB_DIRECTION_RTL, &x, &y)); + g_assert_cmpint (x, ==, 4); + g_assert_cmpint (y, ==, 5); + g_assert (!hb_font_get_glyph_contour_point_for_origin (font2, 3, 7, HB_DIRECTION_LTR, &x, &y)); + g_assert_cmpint (x, ==, 0); + g_assert_cmpint (y, ==, 0); + x = hb_font_get_glyph_h_advance (font2, 1); + g_assert_cmpint (x, ==, 8); + x = hb_font_get_glyph_h_advance (font2, 2); + g_assert_cmpint (x, ==, 0); + + + font3 = hb_font_create_sub_font (font2); + g_assert (hb_font_is_immutable (font2)); + hb_font_destroy (font2); + + /* setup font3 to override scale */ + hb_font_set_scale (font3, 20, 30); + + x = y = 1; + g_assert (hb_font_get_glyph_contour_point_for_origin (font3, 1, 2, HB_DIRECTION_RTL, &x, &y)); + g_assert_cmpint (x, ==, 6*2); + g_assert_cmpint (y, ==, 7*3); + g_assert (hb_font_get_glyph_contour_point_for_origin (font3, 2, 5, HB_DIRECTION_LTR, &x, &y)); + g_assert_cmpint (x, ==, 4*2); + g_assert_cmpint (y, ==, 5*3); + g_assert (!hb_font_get_glyph_contour_point_for_origin (font3, 3, 7, HB_DIRECTION_LTR, &x, &y)); + g_assert_cmpint (x, ==, 0*2); + g_assert_cmpint (y, ==, 0*3); + x = hb_font_get_glyph_h_advance (font3, 1); + g_assert_cmpint (x, ==, 8*2); + x = hb_font_get_glyph_h_advance (font3, 2); + g_assert_cmpint (x, ==, 0*2); + + + hb_font_destroy (font3); +} + + +static void +test_font_empty (void) +{ + g_assert (hb_font_get_empty ()); + g_assert (hb_font_get_empty () == hb_font_create (hb_face_get_empty ())); + g_assert (hb_font_get_empty () == hb_font_create (NULL)); + g_assert (hb_font_get_empty () == hb_font_create_sub_font (NULL)); + g_assert (hb_font_is_immutable (hb_font_get_empty ())); + + g_assert (hb_font_get_face (hb_font_get_empty ()) == hb_face_get_empty ()); + g_assert (hb_font_get_parent (hb_font_get_empty ()) == NULL); +} + +static void +test_font_properties (void) +{ + hb_blob_t *blob; + hb_face_t *face; + hb_font_t *font; + hb_font_t *subfont; + int x_scale, y_scale; + unsigned int x_ppem, y_ppem; + + blob = hb_blob_create (test_data, sizeof (test_data), HB_MEMORY_MODE_READONLY, NULL, NULL); + face = hb_face_create (blob, 0); + hb_blob_destroy (blob); + font = hb_font_create (face); + hb_face_destroy (face); + + + g_assert (hb_font_get_face (font) == face); + g_assert (hb_font_get_parent (font) == NULL); + + + /* Check scale */ + + hb_font_get_scale (font, NULL, NULL); + x_scale = y_scale = 13; + hb_font_get_scale (font, &x_scale, NULL); + g_assert_cmpint (x_scale, ==, 0); + x_scale = y_scale = 13; + hb_font_get_scale (font, NULL, &y_scale); + g_assert_cmpint (y_scale, ==, 0); + x_scale = y_scale = 13; + hb_font_get_scale (font, &x_scale, &y_scale); + g_assert_cmpint (x_scale, ==, 0); + g_assert_cmpint (y_scale, ==, 0); + + hb_font_set_scale (font, 17, 19); + + x_scale = y_scale = 13; + hb_font_get_scale (font, &x_scale, &y_scale); + g_assert_cmpint (x_scale, ==, 17); + g_assert_cmpint (y_scale, ==, 19); + + + /* Check ppem */ + + hb_font_get_ppem (font, NULL, NULL); + x_ppem = y_ppem = 13; + hb_font_get_ppem (font, &x_ppem, NULL); + g_assert_cmpint (x_ppem, ==, 0); + x_ppem = y_ppem = 13; + hb_font_get_ppem (font, NULL, &y_ppem); + g_assert_cmpint (y_ppem, ==, 0); + x_ppem = y_ppem = 13; + hb_font_get_ppem (font, &x_ppem, &y_ppem); + g_assert_cmpint (x_ppem, ==, 0); + g_assert_cmpint (y_ppem, ==, 0); + + hb_font_set_ppem (font, 17, 19); + + x_ppem = y_ppem = 13; + hb_font_get_ppem (font, &x_ppem, &y_ppem); + g_assert_cmpint (x_ppem, ==, 17); + g_assert_cmpint (y_ppem, ==, 19); + + + /* Check immutable */ + + g_assert (!hb_font_is_immutable (font)); + hb_font_make_immutable (font); + g_assert (hb_font_is_immutable (font)); + + hb_font_set_scale (font, 10, 12); + x_scale = y_scale = 13; + hb_font_get_scale (font, &x_scale, &y_scale); + g_assert_cmpint (x_scale, ==, 17); + g_assert_cmpint (y_scale, ==, 19); + + hb_font_set_ppem (font, 10, 12); + x_ppem = y_ppem = 13; + hb_font_get_ppem (font, &x_ppem, &y_ppem); + g_assert_cmpint (x_ppem, ==, 17); + g_assert_cmpint (y_ppem, ==, 19); + + + /* sub_font now */ + subfont = hb_font_create_sub_font (font); + hb_font_destroy (font); + + g_assert (hb_font_get_parent (subfont) == font); + g_assert (hb_font_get_face (subfont) == face); + + /* scale */ + x_scale = y_scale = 13; + hb_font_get_scale (subfont, &x_scale, &y_scale); + g_assert_cmpint (x_scale, ==, 17); + g_assert_cmpint (y_scale, ==, 19); + hb_font_set_scale (subfont, 10, 12); + x_scale = y_scale = 13; + hb_font_get_scale (subfont, &x_scale, &y_scale); + g_assert_cmpint (x_scale, ==, 10); + g_assert_cmpint (y_scale, ==, 12); + x_scale = y_scale = 13; + hb_font_get_scale (font, &x_scale, &y_scale); + g_assert_cmpint (x_scale, ==, 17); + g_assert_cmpint (y_scale, ==, 19); + + /* ppem */ + x_ppem = y_ppem = 13; + hb_font_get_ppem (subfont, &x_ppem, &y_ppem); + g_assert_cmpint (x_ppem, ==, 17); + g_assert_cmpint (y_ppem, ==, 19); + hb_font_set_ppem (subfont, 10, 12); + x_ppem = y_ppem = 13; + hb_font_get_ppem (subfont, &x_ppem, &y_ppem); + g_assert_cmpint (x_ppem, ==, 10); + g_assert_cmpint (y_ppem, ==, 12); + x_ppem = y_ppem = 13; + hb_font_get_ppem (font, &x_ppem, &y_ppem); + g_assert_cmpint (x_ppem, ==, 17); + g_assert_cmpint (y_ppem, ==, 19); + + hb_font_destroy (subfont); +} + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_face_empty); + hb_test_add (test_face_create); + hb_test_add (test_face_createfortables); + + hb_test_add (test_fontfuncs_empty); + hb_test_add (test_fontfuncs_nil); + hb_test_add (test_fontfuncs_subclassing); + + hb_test_add (test_font_empty); + hb_test_add (test_font_properties); + + return hb_test_run(); +}
diff --git a/test/api/test-object.c b/test/api/test-object.c new file mode 100644 index 0000000..66e8d33 --- /dev/null +++ b/test/api/test-object.c
@@ -0,0 +1,367 @@ +/* + * 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_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_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[2]; + + { + 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 (); +}
diff --git a/test/api/test-ot-tag.c b/test/api/test-ot-tag.c new file mode 100644 index 0000000..81b6678 --- /dev/null +++ b/test/api/test-ot-tag.c
@@ -0,0 +1,241 @@ +/* + * 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" + +#include <hb-ot.h> + +/* Unit tests for hb-ot-tag.h */ + + +/* https://www.microsoft.com/typography/otspec/scripttags.htm */ + +static void +test_simple_tags (const char *s, hb_script_t script) +{ + hb_script_t tag; + hb_script_t t1, t2; + + g_test_message ("Testing script %c%c%c%c: tag %s", HB_UNTAG (hb_script_to_iso15924_tag (script)), s); + tag = hb_tag_from_string (s, -1); + + hb_ot_tags_from_script (script, &t1, &t2); + + g_assert_cmphex (t1, ==, tag); + g_assert_cmphex (t2, ==, HB_OT_TAG_DEFAULT_SCRIPT); + + g_assert_cmphex (hb_ot_tag_to_script (tag), ==, script); +} + +static void +test_indic_tags (const char *s1, const char *s2, hb_script_t script) +{ + hb_script_t tag1, tag2; + hb_script_t t1, t2; + + g_test_message ("Testing script %c%c%c%c: new tag %s, old tag %s", HB_UNTAG (hb_script_to_iso15924_tag (script)), s1, s2); + tag1 = hb_tag_from_string (s1, -1); + tag2 = hb_tag_from_string (s2, -1); + + hb_ot_tags_from_script (script, &t1, &t2); + + g_assert_cmphex (t1, ==, tag1); + g_assert_cmphex (t2, ==, tag2); + + g_assert_cmphex (hb_ot_tag_to_script (tag1), ==, script); + g_assert_cmphex (hb_ot_tag_to_script (tag2), ==, script); +} + +static void +test_ot_tag_script_degenerate (void) +{ + hb_script_t t1, t2; + + g_assert_cmphex (HB_TAG_CHAR4 ("DFLT"), ==, HB_OT_TAG_DEFAULT_SCRIPT); + + /* HIRAGANA and KATAKANA both map to 'kana' */ + test_simple_tags ("kana", HB_SCRIPT_KATAKANA); + hb_ot_tags_from_script (HB_SCRIPT_HIRAGANA, &t1, &t2); + g_assert_cmphex (t1, ==, HB_TAG_CHAR4 ("kana")); + g_assert_cmphex (t2, ==, HB_OT_TAG_DEFAULT_SCRIPT); + + test_simple_tags ("DFLT", HB_SCRIPT_INVALID); + + /* Spaces are replaced */ + g_assert_cmphex (hb_ot_tag_to_script (HB_TAG_CHAR4 ("be ")), ==, hb_script_from_string ("Beee", -1)); +} + +static void +test_ot_tag_script_simple (void) +{ + /* Arbitrary non-existent script */ + test_simple_tags ("wwyz", hb_script_from_string ("wWyZ", -1)); + + /* These we don't really care about */ + test_simple_tags ("zyyy", HB_SCRIPT_COMMON); + test_simple_tags ("zinh", HB_SCRIPT_INHERITED); + test_simple_tags ("zzzz", HB_SCRIPT_UNKNOWN); + + test_simple_tags ("arab", HB_SCRIPT_ARABIC); + test_simple_tags ("copt", HB_SCRIPT_COPTIC); + test_simple_tags ("kana", HB_SCRIPT_KATAKANA); + test_simple_tags ("latn", HB_SCRIPT_LATIN); + + /* These are trickier since their OT script tags have space. */ + test_simple_tags ("lao ", HB_SCRIPT_LAO); + test_simple_tags ("yi ", HB_SCRIPT_YI); + /* Unicode-5.0 additions */ + test_simple_tags ("nko ", HB_SCRIPT_NKO); + /* Unicode-5.1 additions */ + test_simple_tags ("vai ", HB_SCRIPT_VAI); + + /* https://www.microsoft.com/typography/otspec160/scripttagsProposed.htm */ + + /* Unicode-5.2 additions */ + test_simple_tags ("mtei", HB_SCRIPT_MEETEI_MAYEK); + /* Unicode-6.0 additions */ + test_simple_tags ("mand", HB_SCRIPT_MANDAIC); +} + +static void +test_ot_tag_script_indic (void) +{ + test_indic_tags ("bng2", "beng", HB_SCRIPT_BENGALI); + test_indic_tags ("dev2", "deva", HB_SCRIPT_DEVANAGARI); + test_indic_tags ("gjr2", "gujr", HB_SCRIPT_GUJARATI); + test_indic_tags ("gur2", "guru", HB_SCRIPT_GURMUKHI); + test_indic_tags ("knd2", "knda", HB_SCRIPT_KANNADA); + test_indic_tags ("mlm2", "mlym", HB_SCRIPT_MALAYALAM); + test_indic_tags ("ory2", "orya", HB_SCRIPT_ORIYA); + test_indic_tags ("tml2", "taml", HB_SCRIPT_TAMIL); + test_indic_tags ("tel2", "telu", HB_SCRIPT_TELUGU); +} + + + +/* https://www.microsoft.com/typography/otspec/languagetags.htm */ + +static void +test_language_two_way (const char *tag_s, const char *lang_s) +{ + hb_language_t lang = hb_language_from_string (lang_s, -1); + hb_tag_t tag = hb_tag_from_string (tag_s, -1); + + g_test_message ("Testing language %s <-> tag %s", lang_s, tag_s); + + g_assert_cmphex (tag, ==, hb_ot_tag_from_language (lang)); + g_assert (lang == hb_ot_tag_to_language (tag)); +} + +static void +test_tag_from_language (const char *tag_s, const char *lang_s) +{ + hb_language_t lang = hb_language_from_string (lang_s, -1); + hb_tag_t tag = hb_tag_from_string (tag_s, -1); + + g_test_message ("Testing language %s -> tag %s", lang_s, tag_s); + + g_assert_cmphex (tag, ==, hb_ot_tag_from_language (lang)); +} + +static void +test_tag_to_language (const char *tag_s, const char *lang_s) +{ + hb_language_t lang = hb_language_from_string (lang_s, -1); + hb_tag_t tag = hb_tag_from_string (tag_s, -1); + + g_test_message ("Testing tag %s -> language %s", tag_s, lang_s); + + g_assert (lang == hb_ot_tag_to_language (tag)); +} + +static void +test_ot_tag_language (void) +{ + g_assert_cmphex (HB_TAG_CHAR4 ("dflt"), ==, HB_OT_TAG_DEFAULT_LANGUAGE); + test_language_two_way ("dflt", NULL); + + test_language_two_way ("ARA", "ar"); + + test_language_two_way ("AZE", "az"); + test_tag_from_language ("AZE", "az-ir"); + test_tag_from_language ("AZE", "az-az"); + + test_language_two_way ("ENG", "en"); + test_tag_from_language ("ENG", "en_US"); + + test_language_two_way ("EVN", "eve"); + + test_language_two_way ("FAR", "fa"); + test_tag_from_language ("FAR", "fa_IR"); + + test_language_two_way ("ZHH", "zh-hk"); /* Chinese (Hong Kong) */ + + test_tag_from_language ("ZHS", "zh-cn"); /* Chinese (China) */ + test_tag_from_language ("ZHS", "zh-sg"); /* Chinese (Singapore) */ + test_tag_from_language ("ZHT", "zh-mo"); /* Chinese (Macao) */ + test_tag_from_language ("ZHT", "zh-tw"); /* Chinese (Taiwan) */ + + test_tag_from_language ("ZHS", "zh"); /* Chinese */ + test_tag_from_language ("ZHS", "zh-xx"); + + test_tag_to_language ("ZHS", "zh-x-hbotzhs"); + test_tag_to_language ("ZHT", "zh-x-hbotzht"); + test_tag_to_language ("ZHP", "zh-x-hbotzhp"); + + test_language_two_way ("ABC", "x-hbotabc"); + test_tag_from_language ("ABC", "asdf-asdf-wer-x-hbotabc-zxc"); + test_tag_from_language ("ABC", "asdf-asdf-wer-x-hbotabc"); + test_tag_from_language ("ABCD", "asdf-asdf-wer-x-hbotabcd"); + + test_tag_from_language ("dflt", "asdf-asdf-wer-x-hbot-zxc"); + + test_tag_from_language ("dflt", "xy"); + test_tag_from_language ("XYZ", "xyz"); /* Unknown ISO 639-3 */ + test_tag_from_language ("XYZ", "xyz-qw"); /* Unknown ISO 639-3 */ + + /* Test that x-hbot overrides the base language */ + test_tag_from_language ("ABC", "fa-x-hbotabc-zxc"); + test_tag_from_language ("ABC", "fa-ir-x-hbotabc-zxc"); + test_tag_from_language ("ABC", "zh-x-hbotabc-zxc"); + test_tag_from_language ("ABC", "zh-cn-x-hbotabc-zxc"); + test_tag_from_language ("ABC", "zh-xy-x-hbotabc-zxc"); + test_tag_from_language ("ABC", "xyz-xy-x-hbotabc-zxc"); +} + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_ot_tag_script_degenerate); + hb_test_add (test_ot_tag_script_simple); + hb_test_add (test_ot_tag_script_indic); + + hb_test_add (test_ot_tag_language); + + return hb_test_run(); +}
diff --git a/test/api/test-shape-complex.c b/test/api/test-shape-complex.c new file mode 100644 index 0000000..8f25d8f --- /dev/null +++ b/test/api/test-shape-complex.c
@@ -0,0 +1,1189 @@ +/* + * Copyright © 2011 Google, Inc. + * Copyright © 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 complex text shaping */ + +/* + * This test provides a framework to test aspects of hb_shape() that are + * font-dependent. Please add tests for any feature that fits that + * description. + */ + +#include <hb-ft.h> + +typedef struct +{ + const char *font_file; + unsigned int face_index; + /* TODO add min/max face version */ +} font_data_t; + +static char * +get_font_file (const font_data_t *font_data) +{ + return g_strdup_printf ("%s/fonts/%s", srcdir (), font_data->font_file); +} + + +typedef struct +{ + char comments[64]; + hb_codepoint_t characters[16]; + hb_codepoint_t glyphs[16]; +} test_data_t; + +typedef struct +{ + const font_data_t font_data; + const test_data_t test_datas[]; +} test_set_t; + +typedef struct +{ + const font_data_t *font_data; + const test_data_t *test_data; +} test_t; + + +static const test_set_t tests_devanagari1 = { + {"raghu.ttf", 0}, + { + { "Ka", + { 0x0915, 0 }, + { 0x0080, 0 } + }, + { "Ka Halant", + { 0x0915, 0x094d, 0 }, + { 0x0080, 0x0051, 0 } + }, + { "Ka Halant Ka", + { 0x0915, 0x094d, 0x0915, 0 }, + { 0x00c8, 0x0080, 0 } + }, + { "Ka MatraI", + { 0x0915, 0x093f, 0 }, + { 0x01d1, 0x0080, 0 } + }, + { "Ra Halant Ka", + { 0x0930, 0x094d, 0x0915, 0 }, + { 0x0080, 0x005b, 0 } + }, + { "Ra Halant Ka MatraI", + { 0x0930, 0x094d, 0x0915, 0x093f, 0 }, + { 0x01d1, 0x0080, 0x005b, 0 } + }, + { "MatraI", + { 0x093f, 0 }, + { 0x01d4, 0x029c, 0 } + }, + { "Ka Nukta", + { 0x0915, 0x093c, 0 }, + { 0x00a4, 0 } + }, + { "Ka Halant Ra", + { 0x0915, 0x094d, 0x0930, 0 }, + { 0x0110, 0 } + }, + { "Ka Halant Ra Halant Ka", + { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0 }, + { 0x0158, 0x0080, 0 } + }, + { "", + { 0x0930, 0x094d, 0x200d, 0 }, + { 0x00e2, 0 } + }, + { "", + { 0x0915, 0x094d, 0x0930, 0x094d, 0x200d, 0 }, + { 0x0158, 0 } + }, + {{0}} + } +}; + +static const test_set_t tests_devanagari2 = { + {"mangal.ttf", 0}, + { + { "Ka", + { 0x0915, 0 }, + { 0x0080, 0 } + }, + { "Ka Halant", + { 0x0915, 0x094d, 0 }, + { 0x0080, 0x0051, 0 } + }, + { "Ka Halant Ka", + { 0x0915, 0x094d, 0x0915, 0 }, + { 0x00c8, 0x0080, 0 } + }, + { "Ka MatraI", + { 0x0915, 0x093f, 0 }, + { 0x01d1, 0x0080, 0 } + }, + { "Ra Halant Ka", + { 0x0930, 0x094d, 0x0915, 0 }, + { 0x0080, 0x005b, 0 } + }, + { "Ra Halant Ka MatraI", + { 0x0930, 0x094d, 0x0915, 0x093f, 0 }, + { 0x01d1, 0x0080, 0x005b, 0 } + }, + { "MatraI", + { 0x093f, 0 }, + { 0x01d4, 0x029c, 0 } + }, + { "Ka Nukta", + { 0x0915, 0x093c, 0 }, + { 0x00a4, 0 } + }, + { "Ka Halant Ra", + { 0x0915, 0x094d, 0x0930, 0 }, + { 0x0110, 0 } + }, + { "Ka Halant Ra Halant Ka", + { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0 }, + { 0x0158, 0x0080, 0 } + }, + { "", + { 0x92b, 0x94d, 0x930, 0 }, + { 0x125, 0 } + }, + { "", + { 0x92b, 0x93c, 0x94d, 0x930, 0 }, + { 0x149, 0 } + }, + {{0}} + } +}; + +static const test_set_t tests_devanagari3 = { + {"Lohit-Devanagari.ttf", 0}, + { + { "Ka Halant Ssa Halant", + { 0x0915, 0x094D, 0x0937, 0x094D, 0}, + { 0x00C4, 0x007D, 0} + }, + { "Ja Halant Nya Halant", + { 0x091C, 0x094D, 0x091E, 0x094D, 0}, + { 0x00C5, 0x007D, 0} + }, + {{0}} + } +}; + +static const test_set_t tests_bengali1 = { + {"AkaashNormal.ttf", 0}, + { + { "Ka", + { 0x0995, 0 }, + { 0x0151, 0 } + }, + { "Ka Halant", + { 0x0995, 0x09cd, 0 }, + { 0x0151, 0x017d, 0 } + }, + { "Ka Halant Ka", + { 0x0995, 0x09cd, 0x0995, 0 }, + { 0x019b, 0 } + }, + { "Ka MatraI", + { 0x0995, 0x09bf, 0 }, + { 0x0173, 0x0151, 0 } + }, + { "Ra Halant Ka", + { 0x09b0, 0x09cd, 0x0995, 0 }, + { 0x0151, 0x0276, 0 } + }, + { "Ra Halant Ka MatraI", + { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0 }, + { 0x0173, 0x0151, 0x0276, 0 } + }, + { "Ka Nukta", + { 0x0995, 0x09bc, 0 }, + { 0x0151, 0x0171, 0 } + }, + { "Ka Halant Ra", + { 0x0995, 0x09cd, 0x09b0, 0 }, + { 0x01f4, 0 } + }, + { "Ka Halant Ra Halant Ka", + { 0x0995, 0x09cd, 0x09b0, 0x09cd, 0x0995, 0 }, + { 0x025c, 0x0276, 0x0151, 0 } + }, + { "Ya + Halant", + { 0x09af, 0x09cd, 0 }, + { 0x016a, 0x017d, 0 } + }, + { "Da Halant Ya -> Da Ya-Phala", + { 0x09a6, 0x09cd, 0x09af, 0 }, + { 0x01e5, 0 } + }, + { "A Halant Ya -> A Ya-phala", + { 0x0985, 0x09cd, 0x09af, 0 }, + { 0x0145, 0x01cf, 0 } + }, + { "Na Halant Ka", + { 0x09a8, 0x09cd, 0x0995, 0 }, + { 0x026f, 0x0151, 0 } + }, + { "Na Halant ZWNJ Ka", + { 0x09a8, 0x09cd, 0x200c, 0x0995, 0 }, + { 0x0164, 0x017d, 0x0151, 0 } + }, + { "Na Halant ZWJ Ka", + { 0x09a8, 0x09cd, 0x200d, 0x0995, 0 }, + { 0x026f, 0x0151, 0 } + }, + { "Ka Halant ZWNJ Ka", + { 0x0995, 0x09cd, 0x200c, 0x0995, 0 }, + { 0x0151, 0x017d, 0x0151, 0 } + }, + { "Ka Halant ZWJ Ka", + { 0x0995, 0x09cd, 0x200d, 0x0995, 0 }, + { 0x025c, 0x0151, 0 } + }, + { "Na Halant Ra", + { 0x09a8, 0x09cd, 0x09b0, 0 }, + { 0x0207, 0 } + }, + { "Na Halant ZWNJ Ra", + { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0 }, + { 0x0164, 0x017d, 0x016b, 0 } + }, + { "Na Halant ZWJ Ra", + { 0x09a8, 0x09cd, 0x200d, 0x09b0, 0 }, + { 0x026f, 0x016b, 0 } + }, + { "Na Halant Ba", + { 0x09a8, 0x09cd, 0x09ac, 0 }, + { 0x022f, 0 } + }, + { "Na Halant ZWNJ Ba", + { 0x09a8, 0x09cd, 0x200c, 0x09ac, 0 }, + { 0x0164, 0x017d, 0x0167, 0 } + }, + { "Na Halant ZWJ Ba", + { 0x09a8, 0x09cd, 0x200d, 0x09ac, 0 }, + { 0x026f, 0x0167, 0 } + }, + { "Na Halant Dha", + { 0x09a8, 0x09cd, 0x09a7, 0 }, + { 0x01d3, 0 } + }, + { "Na Halant ZWNJ Dha", + { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0 }, + { 0x0164, 0x017d, 0x0163, 0 } + }, + { "Na Halant ZWJ Dha", + { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0 }, + { 0x026f, 0x0163, 0 } + }, + { "Ra Halant Ka MatraAU", + { 0x09b0, 0x09cd, 0x0995, 0x09cc, 0 }, + { 0x0179, 0x0151, 0x0276, 0x017e, 0 } + }, + { "Ra Halant Ba Halant Ba", + { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0 }, + { 0x0232, 0x0276, 0 } + }, + { "", + { 0x9b0, 0x9cd, 0x995, 0x9be, 0x982, 0 }, + { 0x151, 0x276, 0x172, 0x143, 0 } + }, + { "", + { 0x9b0, 0x9cd, 0x995, 0x9be, 0x983, 0 }, + { 0x151, 0x276, 0x172, 0x144, 0 } + }, + /* Test decomposed two part matras */ + { "", + { 0x995, 0x9c7, 0x9be, 0 }, + { 0x179, 0x151, 0x172, 0 } + }, + { "", + { 0x995, 0x9c7, 0x9d7, 0 }, + { 0x179, 0x151, 0x17e, 0 } + }, + { "", + { 0x9b0, 0x9cd, 0x9ad, 0 }, + { 0x168, 0x276, 0 } + }, + { "", + { 0x9f0, 0x9cd, 0x9ad, 0 }, + { 0x168, 0x276, 0 } + }, + { "", + { 0x9f1, 0x9cd, 0x9ad, 0 }, + { 0x191, 0x17d, 0x168, 0 } + }, + {{0}} + } +}; + +static const test_set_t tests_bengali2 = { + {"MuktiNarrow.ttf", 0}, + { + { "Ka", + { 0x0995, 0 }, + { 0x0073, 0 } + }, + { "Ka Halant", + { 0x0995, 0x09cd, 0 }, + { 0x00b9, 0 } + }, + { "Ka Halant Ka", + { 0x0995, 0x09cd, 0x0995, 0 }, + { 0x0109, 0 } + }, + { "Ka MatraI", + { 0x0995, 0x09bf, 0 }, + { 0x0095, 0x0073, 0 } + }, + { "Ra Halant Ka", + { 0x09b0, 0x09cd, 0x0995, 0 }, + { 0x0073, 0x00e1, 0 } + }, + { "Ra Halant Ka MatraI", + { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0 }, + { 0x0095, 0x0073, 0x00e1, 0 } + }, + { "MatraI", + { 0x09bf, 0 }, + { 0x0095, 0x01c8, 0 } + }, + { "Ka Nukta", + { 0x0995, 0x09bc, 0 }, + { 0x0073, 0x0093, 0 } + }, + { "Ka Halant Ra", + { 0x0995, 0x09cd, 0x09b0, 0 }, + { 0x00e5, 0 } + }, + { "Ka Halant Ra Halant Ka", + { 0x995, 0x9cd, 0x9b0, 0x9cd, 0x995, 0 }, + { 0x234, 0x24e, 0x73, 0 } + }, + { "Ya + Halant", + { 0x09af, 0x09cd, 0 }, + { 0x00d2, 0 } + }, + { "Da Halant Ya -> Da Ya-Phala", + { 0x09a6, 0x09cd, 0x09af, 0 }, + { 0x0084, 0x00e2, 0 } + }, + { "A Halant Ya -> A Ya-phala", + { 0x0985, 0x09cd, 0x09af, 0 }, + { 0x0067, 0x00e2, 0 } + }, + { "Na Halant Ka", + { 0x09a8, 0x09cd, 0x0995, 0 }, + { 0x0188, 0 } + }, + { "Na Halant ZWNJ Ka", + { 0x9a8, 0x9cd, 0x200c, 0x995, 0 }, + { 0xcc, 0x73, 0 } + }, + { "Na Halant ZWJ Ka", + { 0x9a8, 0x9cd, 0x200d, 0x995, 0 }, + { 0x247, 0x73, 0 } + }, + { "Ka Halant ZWNJ Ka", + { 0x9a8, 0x9cd, 0x200d, 0x995, 0 }, + { 0x247, 0x73, 0 } + }, + { "Ka Halant ZWJ Ka", + { 0x9a8, 0x9cd, 0x200d, 0x995, 0 }, + { 0x247, 0x73, 0 } + }, + { "Na Halant Ra", + { 0x09a8, 0x09cd, 0x09b0, 0 }, + { 0x00f8, 0 } + }, + { "Na Halant ZWNJ Ra", + { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0 }, + { 0xcc, 0x8d, 0 } + }, + { "Na Halant ZWJ Ra", + { 0x9a8, 0x9cd, 0x200d, 0x9b0, 0 }, + { 0x247, 0x8d, 0 } + }, + { "Na Halant Ba", + { 0x09a8, 0x09cd, 0x09ac, 0 }, + { 0x0139, 0 } + }, + { "Na Halant ZWNJ Ba", + { 0x9a8, 0x9cd, 0x200c, 0x9ac, 0 }, + { 0xcc, 0x89, 0 } + }, + { "Na Halant ZWJ Ba", + { 0x9a8, 0x9cd, 0x200d, 0x9ac, 0 }, + { 0x247, 0x89, 0 } + }, + { "Na Halant Dha", + { 0x09a8, 0x09cd, 0x09a7, 0 }, + { 0x0145, 0 } + }, + { "Na Halant ZWNJ Dha", + { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0 }, + { 0xcc, 0x85, 0 } + }, + { "Na Halant ZWJ Dha", + { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0 }, + { 0x247, 0x85, 0 } + }, + { "Ra Halant Ka MatraAU", + { 0x9b0, 0x9cd, 0x995, 0x9cc, 0 }, + { 0x232, 0x73, 0xe1, 0xa0, 0 } + }, + { "Ra Halant Ba Halant Ba", + { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0 }, + { 0x013b, 0x00e1, 0 } + }, + {{0}} + } +}; + +static const test_set_t tests_bengali3 = { + {"LikhanNormal.ttf", 0}, + { + { "", + { 0x09a8, 0x09cd, 0x09af, 0 }, + { 0x01ca, 0 } + }, + { "", + { 0x09b8, 0x09cd, 0x09af, 0 }, + { 0x020e, 0 } + }, + { "", + { 0x09b6, 0x09cd, 0x09af, 0 }, + { 0x01f4, 0 } + }, + { "", + { 0x09b7, 0x09cd, 0x09af, 0 }, + { 0x01fe, 0 } + }, + { "", + { 0x09b0, 0x09cd, 0x09a8, 0x09cd, 0x200d, 0 }, + { 0x10b, 0x167, 0 } + }, + { "", + { 0x9b0, 0x9cd, 0x9ad, 0 }, + { 0xa1, 0x167, 0 } + }, + { "", + { 0x9f0, 0x9cd, 0x9ad, 0 }, + { 0xa1, 0x167, 0 } + }, + { "", + { 0x9f1, 0x9cd, 0x9ad, 0 }, + { 0x11c, 0xa1, 0 } + }, + {{0}} + } +}; + +static const test_set_t tests_gurmukhi = { + {"lohit_pa.ttf", 0}, + { + { "", + { 0xA15, 0xA4D, 0xa39, 0 }, + { 0x3b, 0x8b, 0 } + }, + {{0}} + } +}; + +static const test_set_t tests_oriya = { + {"utkalm.ttf", 0}, + { + { "", + { 0xb15, 0xb4d, 0xb24, 0xb4d, 0xb30, 0 }, + { 0x150, 0x125, 0 } + }, + { "", + { 0xb24, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0 }, + { 0x151, 0x120, 0 } + }, + { "", + { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0 }, + { 0x152, 0x120, 0 } + }, + { "", + { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0 }, + { 0x152, 0x120, 0 } + }, + { "", + { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0 }, + { 0x176, 0 } + }, + { "", + { 0xb38, 0xb4d, 0xb24, 0xb4d, 0xb30, 0 }, + { 0x177, 0 } + }, + { "", + { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0xb4d, 0xb2f, 0 }, + { 0x176, 0x124, 0 } + }, + {{0}} + } +}; + +static const test_set_t tests_tamil = { + {"akruti1.ttf", 0}, + { + { "", + { 0x0b95, 0x0bc2, 0 }, + { 0x004e, 0 } + }, + { "", + { 0x0bae, 0x0bc2, 0 }, + { 0x009e, 0 } + }, + { "", + { 0x0b9a, 0x0bc2, 0 }, + { 0x0058, 0 } + }, + { "", + { 0x0b99, 0x0bc2, 0 }, + { 0x0053, 0 } + }, + { "", + { 0x0bb0, 0x0bc2, 0 }, + { 0x00a8, 0 } + }, + { "", + { 0x0ba4, 0x0bc2, 0 }, + { 0x008e, 0 } + }, + { "", + { 0x0b9f, 0x0bc2, 0 }, + { 0x0062, 0 } + }, + { "", + { 0x0b95, 0x0bc6, 0 }, + { 0x000a, 0x0031, 0 } + }, + { "", + { 0x0b95, 0x0bca, 0 }, + { 0x000a, 0x0031, 0x0007, 0 } + }, + { "", + { 0x0b95, 0x0bc6, 0x0bbe, 0 }, + { 0x000a, 0x0031, 0x007, 0 } + }, + { "", + { 0x0b95, 0x0bcd, 0x0bb7, 0 }, + { 0x0049, 0 } + }, + { "", + { 0x0b95, 0x0bcd, 0x0bb7, 0x0bca, 0 }, + { 0x000a, 0x0049, 0x007, 0 } + }, + { "", + { 0x0b95, 0x0bcd, 0x0bb7, 0x0bc6, 0x0bbe, 0 }, + { 0x000a, 0x0049, 0x007, 0 } + }, + { "", + { 0x0b9f, 0x0bbf, 0 }, + { 0x005f, 0 } + }, + { "", + { 0x0b9f, 0x0bc0, 0 }, + { 0x0060, 0 } + }, + { "", + { 0x0bb2, 0x0bc0, 0 }, + { 0x00ab, 0 } + }, + { "", + { 0x0bb2, 0x0bbf, 0 }, + { 0x00aa, 0 } + }, + { "", + { 0x0bb0, 0x0bcd, 0 }, + { 0x00a4, 0 } + }, + { "", + { 0x0bb0, 0x0bbf, 0 }, + { 0x00a5, 0 } + }, + { "", + { 0x0bb0, 0x0bc0, 0 }, + { 0x00a6, 0 } + }, + { "", + { 0x0b83, 0 }, + { 0x0025, 0 } + }, + { "", + { 0x0b83, 0x0b95, 0 }, + { 0x0025, 0x0031, 0 } + }, + {{0}} + } +}; + +static const test_set_t tests_telugu = { + {"Pothana2000.ttf", 0}, + { + { "", + { 0xc15, 0xc4d, 0 }, + { 0xbb, 0 } + }, + { "", + { 0xc15, 0xc4d, 0xc37, 0 }, + { 0x4b, 0 } + }, + { "", + { 0xc15, 0xc4d, 0xc37, 0xc4d, 0 }, + { 0xe0, 0 } + }, + { "", + { 0xc15, 0xc4d, 0xc37, 0xc4d, 0xc23, 0 }, + { 0x4b, 0x91, 0 } + }, + { "", + { 0xc15, 0xc4d, 0xc30, 0 }, + { 0x5a, 0xb2, 0 } + }, + { "", + { 0xc15, 0xc4d, 0xc30, 0xc4d, 0 }, + { 0xbb, 0xb2, 0 } + }, + { "", + { 0xc15, 0xc4d, 0xc30, 0xc4d, 0xc15, 0 }, + { 0x5a, 0xb2, 0x83, 0 } + }, + { "", + { 0xc15, 0xc4d, 0xc30, 0xc3f, 0 }, + { 0xe2, 0xb2, 0 } + }, + { "", + { 0xc15, 0xc4d, 0xc15, 0xc48, 0 }, + { 0xe6, 0xb3, 0x83, 0 } + }, + { "", + { 0xc15, 0xc4d, 0xc30, 0xc48, 0 }, + { 0xe6, 0xb3, 0x9f, 0 } + }, + { "", + { 0xc15, 0xc46, 0xc56, 0 }, + { 0xe6, 0xb3, 0 } + }, + {{0}} + } +}; + +static const test_set_t tests_kannada1 = { + {"Sampige.ttf", 0}, + { + { "", + { 0x0ca8, 0x0ccd, 0x0ca8, 0 }, + { 0x0049, 0x00ba, 0 } + }, + { "", + { 0x0ca8, 0x0ccd, 0x0ca1, 0 }, + { 0x0049, 0x00b3, 0 } + }, + { "", + { 0x0caf, 0x0cc2, 0 }, + { 0x004f, 0x005d, 0 } + }, + { "", + { 0x0ce0, 0 }, + { 0x006a, 0 } + }, + { "", + { 0x0ce6, 0x0ce7, 0x0ce8, 0 }, + { 0x006b, 0x006c, 0x006d, 0 } + }, + { "", + { 0x0cb5, 0x0ccb, 0 }, + { 0x015f, 0x0067, 0 } + }, + { "", + { 0x0cb0, 0x0ccd, 0x0cae, 0 }, + { 0x004e, 0x0082, 0 } + }, + { "", + { 0x0cb0, 0x0ccd, 0x0c95, 0 }, + { 0x0036, 0x0082, 0 } + }, + { "", + { 0x0c95, 0x0ccd, 0x0cb0, 0 }, + { 0x0036, 0x00c1, 0 } + }, + { "", + { 0x0cb0, 0x0ccd, 0x200d, 0x0c95, 0 }, + { 0x0050, 0x00a7, 0 } + }, + {{0}} + } +}; + +static const test_set_t tests_kannada2 = { + {"tunga.ttf", 0}, + { + { "", + { 0x0cb7, 0x0cc6, 0 }, + { 0x00b0, 0x006c, 0 } + }, + { "", + { 0x0cb7, 0x0ccd, 0 }, + { 0x0163, 0 } + }, + { "", + { 0xc95, 0xcbf, 0xcd5, 0 }, + { 0x114, 0x73, 0 } + }, + { "", + { 0xc95, 0xcc6, 0xcd5, 0 }, + { 0x90, 0x6c, 0x73, 0 } + }, + { "", + { 0xc95, 0xcc6, 0xcd6, 0 }, + { 0x90, 0x6c, 0x74, 0 } + }, + { "", + { 0xc95, 0xcc6, 0xcc2, 0 }, + { 0x90, 0x6c, 0x69, 0 } + }, + { "", + { 0xc95, 0xcca, 0xcd5, 0 }, + { 0x90, 0x6c, 0x69, 0x73, 0 } + }, + {{0}} + } +}; + +static const test_set_t tests_malayalam1 = { + {"AkrutiMal2Normal.ttf", 0}, + { + { "", + { 0x0d15, 0x0d46, 0 }, + { 0x005e, 0x0034, 0 } + }, + { "", + { 0x0d15, 0x0d47, 0 }, + { 0x005f, 0x0034, 0 } + }, + { "", + { 0x0d15, 0x0d4b, 0 }, + { 0x005f, 0x0034, 0x0058, 0 } + }, + { "", + { 0x0d15, 0x0d48, 0 }, + { 0x0060, 0x0034, 0 } + }, + { "", + { 0x0d15, 0x0d4a, 0 }, + { 0x005e, 0x0034, 0x0058, 0 } + }, + { "", + { 0x0d30, 0x0d4d, 0x0d15, 0 }, + { 0x009e, 0x0034, 0 } + }, + { "", + { 0x0d15, 0x0d4d, 0x0d35, 0 }, + { 0x0034, 0x007a, 0 } + }, + { "", + { 0x0d15, 0x0d4d, 0x0d2f, 0 }, + { 0x0034, 0x00a2, 0 } + }, + { "", + { 0x0d1f, 0x0d4d, 0x0d1f, 0 }, + { 0x0069, 0 } + }, + { "", + { 0x0d26, 0x0d4d, 0x0d26, 0 }, + { 0x0074, 0 } + }, + { "", + { 0x0d30, 0x0d4d, 0 }, + { 0x009e, 0 } + }, + { "", + { 0x0d30, 0x0d4d, 0x200c, 0 }, + { 0x009e, 0 } + }, + { "", + { 0x0d30, 0x0d4d, 0x200d, 0 }, + { 0x009e, 0 } + }, + { "", + { 0xd15, 0xd46, 0xd3e, 0 }, + { 0x5e, 0x34, 0x58, 0 } + }, + { "", + { 0xd15, 0xd47, 0xd3e, 0 }, + { 0x5f, 0x34, 0x58, 0 } + }, + { "", + { 0xd15, 0xd46, 0xd57, 0 }, + { 0x5e, 0x34, 0x65, 0 } + }, + { "", + { 0xd15, 0xd57, 0 }, + { 0x34, 0x65, 0 } + }, + { "", + { 0xd1f, 0xd4d, 0xd1f, 0xd41, 0xd4d, 0 }, + { 0x69, 0x5b, 0x64, 0 } + }, + + {{0}} + } +}; + +static const test_set_t tests_malayalam2 = { + {"Rachana.ttf", 0}, + { + { "", + { 0xd37, 0xd4d, 0xd1f, 0xd4d, 0xd30, 0xd40, 0 }, + { 0x385, 0xa3, 0 } + }, + { "", + { 0xd2f, 0xd4d, 0xd15, 0xd4d, 0xd15, 0xd41, 0 }, + { 0x2ff, 0 } + }, + { "", + { 0xd33, 0xd4d, 0xd33, 0 }, + { 0x3f8, 0 } + }, + { "", + { 0xd2f, 0xd4d, 0xd15, 0xd4d, 0xd15, 0xd41, 0 }, + { 0x2ff, 0 } + }, + { "", + { 0xd30, 0xd4d, 0x200d, 0xd35, 0xd4d, 0xd35, 0 }, + { 0xf3, 0x350, 0 } + }, + + {{0}} + } +}; + +static const test_set_t tests_sinhala = { + {"FM-MalithiUW46.ttf", 0}, + { + { "", + { 0xd9a, 0xdd9, 0xdcf, 0 }, + { 0x4a, 0x61, 0x42, 0 } + }, + { "", + { 0xd9a, 0xdd9, 0xddf, 0 }, + { 0x4a, 0x61, 0x50, 0 } + }, + { "", + { 0xd9a, 0xdd9, 0xdca, 0 }, + { 0x4a, 0x62, 0 } + }, + { "", + { 0xd9a, 0xddc, 0xdca, 0 }, + { 0x4a, 0x61, 0x42, 0x41, 0 } + }, + { "", + { 0xd9a, 0xdda, 0 }, + { 0x4a, 0x62, 0 } + }, + { "", + { 0xd9a, 0xddd, 0 }, + { 0x4a, 0x61, 0x42, 0x41, 0 } + }, + {{0}} + } +}; + +static const test_set_t tests_khmer = { + {"KhmerOS.ttf", 0}, + { + { "", + { 0x179a, 0x17cd, 0 }, + { 0x24c, 0x27f, 0 } + }, + { "", + { 0x179f, 0x17c5, 0 }, + { 0x273, 0x203, 0 } + }, + { "", + { 0x1790, 0x17d2, 0x1784, 0x17c3, 0 }, + { 0x275, 0x242, 0x182, 0 } + }, + { "", + { 0x179a, 0 }, + { 0x24c, 0 } + }, + { "", + { 0x1781, 0x17d2, 0x1798, 0x17c2, 0 }, + { 0x274, 0x233, 0x197, 0 } + }, + { "", + { 0x1798, 0x17b6, 0 }, + { 0x1cb, 0 } + }, + { "", + { 0x179a, 0x17b8, 0 }, + { 0x24c, 0x26a, 0 } + }, + { "", + { 0x1787, 0x17b6, 0 }, + { 0x1ba, 0 } + }, + { "", + { 0x1798, 0x17d2, 0x1796, 0x17bb, 0 }, + { 0x24a, 0x195, 0x26d, 0 } + }, + {{0}} + } +}; + +static const test_set_t tests_nko = { + {"DejaVuSans.ttf", 0}, + { + { "", + { 0x7ca, 0 }, + { 0x5c1, 0 } + }, + { "", + { 0x7ca, 0x7ca, 0 }, + { 0x14d9, 0x14db, 0 } + }, + { "", + { 0x7ca, 0x7fa, 0x7ca, 0 }, + { 0x14d9, 0x5ec, 0x14db, 0 } + }, + { "", + { 0x7ca, 0x7f3, 0x7ca, 0 }, + { 0x14d9, 0x5e7, 0x14db, 0 } + }, + { "", + { 0x7ca, 0x7f3, 0x7fa, 0x7ca, 0 }, + { 0x14d9, 0x5ec, 0x5e7, 0x14db, 0 } + }, + {{0}} + } +}; + +static const test_set_t tests_linearb = { + {"penuture.ttf", 0}, + { + { "", + { 0xd800, 0xdc01, 0xd800, 0xdc02, 0xd800, 0xdc03, 0 }, + { 0x5, 0x6, 0x7, 0 }, + }, + {{0}} + } +}; + + + + + +typedef struct { + FT_Library ft_library; + FT_Face ft_face; + hb_font_t *font; +} ft_fixture_t; + +static void +ft_fixture_init (ft_fixture_t *f, gconstpointer user_data) +{ + const test_t *test = user_data; + char *font_file = get_font_file (test->font_data); + FT_Error err; + + FT_Init_FreeType (&f->ft_library); + + err = FT_New_Face (f->ft_library, font_file, test->font_data->face_index, &f->ft_face); + g_assert_cmpint (err, ==, 0); + + f->font = hb_ft_font_create (f->ft_face, NULL); + + g_free (font_file); +} + +static void +ft_fixture_finish (ft_fixture_t *f, gconstpointer user_data) +{ + hb_font_destroy (f->font); + + FT_Done_Face (f->ft_face); + FT_Done_FreeType (f->ft_library); +} + +static void +test_shape_complex (ft_fixture_t *f, gconstpointer user_data) +{ + const test_t *test = user_data; + const test_data_t *data = test->test_data; + hb_buffer_t *buffer; + unsigned int i, len, expected_len; + hb_glyph_info_t *glyphs; + hb_glyph_position_t *pos; + hb_bool_t fail; + GString *str; + + g_assert (f->font); + + if (data->comments[0]) + g_test_message ("Test comments: %s", data->comments); + + buffer = hb_buffer_create (); + for (len = 0; data->characters[len]; len++) ; + hb_buffer_add_utf32 (buffer, data->characters, len, 0, len); + + hb_shape (f->font, buffer, NULL, 0); + + for (len = 0; data->glyphs[len]; len++) ; + expected_len = len; + + glyphs = hb_buffer_get_glyph_infos (buffer, &len); + pos = hb_buffer_get_glyph_positions (buffer, NULL); + fail = len != expected_len; + if (!fail) + for (i = 0; i < len; i++) + if (glyphs[i].codepoint != data->glyphs[i]) { + fail = TRUE; + break; + } + + str = g_string_new (""); + for (i = 0; i < expected_len; i++) + g_string_append_printf (str, " %4d", data->glyphs[i]); + g_test_message ("Expected glyphs: %s", str->str); + g_string_truncate (str, 0); + for (i = 0; i < len; i++) + g_string_append_printf (str, " %4d", glyphs[i].codepoint); + g_test_message ("Received glyphs: %s", str->str); + g_string_truncate (str, 0); + for (i = 0; i < len; i++) + g_string_append_printf (str, " %d", glyphs[i].cluster); + g_test_message ("Received clusters: %s", str->str); + g_string_truncate (str, 0); + for (i = 0; i < len; i++) + g_string_append_printf (str, " (%d,%d)+(%d,%d)", + pos[i].x_offset, + pos[i].y_offset, + pos[i].x_advance, + pos[i].y_advance); + g_test_message ("Received positions: %s", str->str); + g_string_free (str, TRUE); + + if (fail) { + g_test_message ("FAIL"); + /* The glib test framework is useless, lets not fail for now, + * we can grep for FAIL/PASS and count manually. Sigh... */ + /*g_test_fail ();*/ + } else + g_test_message ("PASS"); + + hb_buffer_destroy (buffer); +} + +static void +test_shape_complex_skipped (gconstpointer user_data) +{ + const test_t *test = user_data; + const test_data_t *data = test->test_data; + + if (data->comments[0]) + g_test_message ("Test comments: %s", data->comments); + + g_test_message ("Skipping test"); +} + + +static void +add_test_set (const test_set_t *test_set, const char *set_name) +{ + const test_data_t *data; + char *font_file; + hb_bool_t skip; + + font_file = get_font_file (&test_set->font_data); + skip = !g_file_test (font_file, G_FILE_TEST_EXISTS); + g_free (font_file); + + for (data = test_set->test_datas; data->characters[0]; data++) { + char *flavor; + GString *str; + const hb_codepoint_t *p; + + test_t *test = g_slice_new0 (test_t); + test->font_data = &test_set->font_data; + test->test_data = data; + + str = g_string_new ("<"); + for (p = data->characters; *p; p++) + g_string_append_printf (str, "%04X,", *p); + str->str[str->len - 1] = '>'; + + flavor = g_strdup_printf ("%s/%s/%ld:%s", set_name, test_set->font_data.font_file, data - test_set->test_datas, str->str); + + g_string_free (str, TRUE); + + if (skip) + hb_test_add_data_flavor ((const void *) test, flavor, test_shape_complex_skipped); + else + hb_test_add_fixture_flavor (ft_fixture, (const void *) test, flavor, test_shape_complex); + + g_free (flavor); + } +} + + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + +#define TEST_SET(name) add_test_set (&tests_##name, #name) + + TEST_SET (devanagari1); + TEST_SET (devanagari2); + TEST_SET (devanagari3); + TEST_SET (bengali1); + TEST_SET (bengali2); + TEST_SET (bengali3); + TEST_SET (gurmukhi); + TEST_SET (oriya); + TEST_SET (tamil); + TEST_SET (telugu); + TEST_SET (kannada1); + TEST_SET (kannada2); + TEST_SET (malayalam1); + TEST_SET (malayalam2); + TEST_SET (sinhala); + + TEST_SET (khmer); + + TEST_SET (nko); + TEST_SET (linearb); + + return hb_test_run(); +}
diff --git a/test/api/test-shape.c b/test/api/test-shape.c new file mode 100644 index 0000000..ccf6eed --- /dev/null +++ b/test/api/test-shape.c
@@ -0,0 +1,165 @@ +/* + * 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-shape.h */ + +/* + * This test provides a framework to test aspects of hb_shape() that are + * font-independent. Please add tests for any feature that fits that + * description. + */ + +/* TODO Make this test data-driven and add some real test data */ +/* TODO Test positions too. And test non-native direction. Test commit 2e18c6dbdfb */ + + +static const char test_data[] = "test\0data"; + +static hb_position_t +glyph_h_advance_func (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + void *user_data) +{ + switch (glyph) { + case 1: return 10; + case 2: return 6; + case 3: return 5; + } + return 0; +} + +static hb_bool_t +glyph_func (hb_font_t *font, void *font_data, + hb_codepoint_t unicode, hb_codepoint_t variant_selector, + hb_codepoint_t *glyph, + void *user_data) +{ + switch (unicode) { + case 'T': *glyph = 1; return TRUE; + case 'e': *glyph = 2; return TRUE; + case 's': *glyph = 3; return TRUE; + } + return FALSE; +} + +static hb_position_t +glyph_h_kerning_func (hb_font_t *font, void *font_data, + hb_codepoint_t left, hb_codepoint_t right, + void *user_data) +{ + if (left == 1 && right == 2) + return -2; + + return 0; +} + +static const char TesT[] = "TesT"; + +static void +test_shape (void) +{ + hb_blob_t *blob; + hb_face_t *face; + hb_font_funcs_t *ffuncs; + hb_font_t *font; + hb_buffer_t *buffer; + unsigned int len; + hb_glyph_info_t *glyphs; + hb_glyph_position_t *positions; + + blob = hb_blob_create (test_data, sizeof (test_data), HB_MEMORY_MODE_READONLY, NULL, NULL); + face = hb_face_create (blob, 0); + hb_blob_destroy (blob); + font = hb_font_create (face); + hb_face_destroy (face); + hb_font_set_scale (font, 10, 10); + + ffuncs = hb_font_funcs_create (); + hb_font_funcs_set_glyph_h_advance_func (ffuncs, glyph_h_advance_func, NULL, NULL); + hb_font_funcs_set_glyph_func (ffuncs, glyph_func, NULL, NULL); + hb_font_funcs_set_glyph_h_kerning_func (ffuncs, glyph_h_kerning_func, NULL, NULL); + hb_font_set_funcs (font, ffuncs, NULL, NULL); + hb_font_funcs_destroy (ffuncs); + + buffer = hb_buffer_create (); + hb_buffer_set_direction (buffer, HB_DIRECTION_LTR); + hb_buffer_add_utf8 (buffer, TesT, 4, 0, 4); + + hb_shape (font, buffer, NULL, 0); + + len = hb_buffer_get_length (buffer); + glyphs = hb_buffer_get_glyph_infos (buffer, NULL); + positions = hb_buffer_get_glyph_positions (buffer, NULL); + + { + const hb_codepoint_t output_glyphs[] = {1, 2, 3, 1}; + const hb_position_t output_x_advances[] = {9, 5, 5, 10}; + const hb_position_t output_x_offsets[] = {0, -1, 0, 0}; + unsigned int i; + g_assert_cmpint (len, ==, 4); + for (i = 0; i < len; i++) { + g_assert_cmphex (glyphs[i].codepoint, ==, output_glyphs[i]); + g_assert_cmphex (glyphs[i].cluster, ==, i); + } + for (i = 0; i < len; i++) { + g_assert_cmpint (output_x_advances[i], ==, positions[i].x_advance); + g_assert_cmpint (output_x_offsets [i], ==, positions[i].x_offset); + g_assert_cmpint (0, ==, positions[i].y_advance); + g_assert_cmpint (0, ==, positions[i].y_offset); + } + } + + hb_buffer_destroy (buffer); + hb_font_destroy (font); +} + +static void +test_shape_list (void) +{ + const char **shapers = hb_shape_list_shapers (); + + unsigned int i; + for (i = 0; shapers[i]; i++) + ; + + g_assert_cmpint (i, >, 1); + g_assert (!strcmp (shapers[i - 1], "fallback")); +} + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_shape); + /* TODO test fallback shaper */ + /* TODO test shaper_full */ + hb_test_add (test_shape_list); + + return hb_test_run(); +}
diff --git a/test/api/test-unicode.c b/test/api/test-unicode.c new file mode 100644 index 0000000..a420bf3 --- /dev/null +++ b/test/api/test-unicode.c
@@ -0,0 +1,887 @@ +/* + * Copyright © 2011 Codethink Limited + * 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. + * + * Codethink Author(s): Ryan Lortie + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-test.h" + +/* Unit tests for hb-unicode.h */ +/* Unit tests for hb-glib.h */ +/* Unit tests for hb-icu.h */ + + +#ifdef HAVE_GLIB +#include <hb-glib.h> +#endif +#ifdef HAVE_ICU +#include <hb-icu.h> +#endif + + +/* Some useful stuff */ + +#define MAGIC0 0x12345678 +#define MAGIC1 0x76543210 + +typedef struct { + int value; + gboolean freed; +} data_t; + +static void free_up (void *p) +{ + data_t *data = (data_t *) p; + + g_assert (data->value == MAGIC0 || data->value == MAGIC1); + g_assert (!data->freed); + data->freed = TRUE; +} + +static hb_script_t +simple_get_script (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t codepoint, + void *user_data) +{ + data_t *data = (data_t *) user_data; + + g_assert (hb_unicode_funcs_get_parent (ufuncs) != NULL); + g_assert_cmphex (data->value, ==, MAGIC0); + g_assert (!data->freed); + + if ('a' <= codepoint && codepoint <= 'z') + return HB_SCRIPT_LATIN; + else + return HB_SCRIPT_UNKNOWN; +} + +static hb_script_t +a_is_for_arabic_get_script (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t codepoint, + void *user_data) +{ + data_t *data = (data_t *) user_data; + + g_assert (hb_unicode_funcs_get_parent (ufuncs) != NULL); + g_assert_cmphex (data->value, ==, MAGIC1); + g_assert (!data->freed); + + if (codepoint == 'a') { + return HB_SCRIPT_ARABIC; + } else { + hb_unicode_funcs_t *parent = hb_unicode_funcs_get_parent (ufuncs); + + return hb_unicode_script (parent, codepoint); + } +} + + + +/* Check all properties */ + +/* Some of the following tables where adapted from glib/glib/tests/utf8-misc.c. + * The license is compatible. */ + +typedef struct { + hb_codepoint_t unicode; + unsigned int value; +} test_pair_t; + +static const test_pair_t combining_class_tests[] = +{ + { 0x0020, 0 }, + { 0x0334, 1 }, + { 0x093C, 7 }, + { 0x3099, 8 }, + { 0x094D, 9 }, + { 0x05B0, 10 }, + { 0x05B1, 11 }, + { 0x05B2, 12 }, + { 0x05B3, 13 }, + { 0x05B4, 14 }, + { 0x05B5, 15 }, + { 0x05B6, 16 }, + { 0x05B7, 17 }, + { 0x05B8, 18 }, + { 0x05B9, 19 }, + { 0x05BB, 20 }, + { 0x05BC, 21 }, + { 0x05BD, 22 }, + { 0x05BF, 23 }, + { 0x05C1, 24 }, + { 0x05C2, 25 }, + { 0xFB1E, 26 }, + { 0x064B, 27 }, + { 0x064C, 28 }, + { 0x064D, 29 }, + /* ... */ + { 0x05AE, 228 }, + { 0x0300, 230 }, + { 0x302C, 232 }, + { 0x0362, 233 }, + { 0x0360, 234 }, + { 0x0345, 240 }, + + { 0x111111, 0 } +}; +static const test_pair_t combining_class_tests_more[] = +{ + /* Unicode-5.1 character additions */ + { 0x1DCD, 234 }, + + /* Unicode-5.2 character additions */ + { 0xA8E0, 230 }, + + /* Unicode-6.0 character additions */ + { 0x135D, 230 }, + + { 0x111111, 0 } +}; + +static const test_pair_t eastasian_width_tests[] = +{ + /* Neutral */ + { 0x0000, 1 }, + { 0x0483, 1 }, + { 0x0641, 1 }, + { 0xFFFC, 1 }, + { 0x10000, 1 }, + { 0xE0001, 1 }, + + /* Narrow */ + { 0x0020, 1 }, + { 0x0041, 1 }, + { 0x27E6, 1 }, + + /* Halfwidth */ + { 0x20A9, 1 }, + { 0xFF61, 1 }, + { 0xFF69, 1 }, + { 0xFFEE, 1 }, + + /* Ambiguous */ + { 0x00A1, 1 }, + { 0x00D8, 1 }, + { 0x02DD, 1 }, + { 0xE0100, 1 }, + { 0x100000, 1 }, + + /* Fullwidth */ + { 0x3000, 2 }, + { 0xFF60, 2 }, + + /* Wide */ + { 0x2329, 2 }, + { 0x3001, 2 }, + { 0xFE69, 2 }, + { 0x30000, 2 }, + { 0x3FFFD, 2 }, + + { 0x111111, 1 } +}; +static const test_pair_t eastasian_width_tests_more[] = +{ + /* Default Wide blocks */ + { 0x4DBF, 2 }, + { 0x9FFF, 2 }, + { 0xFAFF, 2 }, + { 0x2A6DF, 2 }, + { 0x2B73F, 2 }, + { 0x2B81F, 2 }, + { 0x2FA1F, 2 }, + + /* Uniode-5.2 character additions */ + /* Wide */ + { 0x115F, 2 }, + + /* Uniode-6.0 character additions */ + /* Wide */ + { 0x2B740, 2 }, + { 0x1B000, 2 }, + + { 0x111111, 1 } +}; + +static const test_pair_t general_category_tests[] = +{ + { 0x000D, HB_UNICODE_GENERAL_CATEGORY_CONTROL }, + { 0x200E, HB_UNICODE_GENERAL_CATEGORY_FORMAT }, + { 0x0378, HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED }, + { 0xE000, HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE }, + { 0xD800, HB_UNICODE_GENERAL_CATEGORY_SURROGATE }, + { 0x0061, HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER }, + { 0x02B0, HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER }, + { 0x3400, HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER }, + { 0x01C5, HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER }, + { 0xFF21, HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER }, + { 0x0903, HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK }, + { 0x20DD, HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK }, + { 0xA806, HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK }, + { 0xFF10, HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER }, + { 0x16EE, HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER }, + { 0x17F0, HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER }, + { 0x005F, HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION }, + { 0x058A, HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION }, + { 0x0F3B, HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION }, + { 0x2019, HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION }, + { 0x2018, HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION }, + { 0x2016, HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION }, + { 0x0F3A, HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION }, + { 0x20A0, HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL }, + { 0x309B, HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL }, + { 0xFB29, HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL }, + { 0x00A6, HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL }, + { 0x2028, HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR }, + { 0x2029, HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR }, + { 0x202F, HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR }, + + { 0x111111, HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED } +}; +static const test_pair_t general_category_tests_more[] = +{ + /* Unicode-5.2 character additions */ + { 0x1F131, HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL }, + + /* Unicode-6.0 character additions */ + { 0x0620, HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER }, + + { 0x111111, HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED } +}; + +static const test_pair_t mirroring_tests[] = +{ + /* Some characters that do NOT mirror */ + { 0x0020, 0x0020 }, + { 0x0041, 0x0041 }, + { 0x00F0, 0x00F0 }, + { 0x27CC, 0x27CC }, + { 0xE01EF, 0xE01EF }, + { 0x1D7C3, 0x1D7C3 }, + { 0x100000, 0x100000 }, + + /* Some characters that do mirror */ + { 0x0029, 0x0028 }, + { 0x0028, 0x0029 }, + { 0x003E, 0x003C }, + { 0x003C, 0x003E }, + { 0x005D, 0x005B }, + { 0x005B, 0x005D }, + { 0x007D, 0x007B }, + { 0x007B, 0x007D }, + { 0x00BB, 0x00AB }, + { 0x00AB, 0x00BB }, + { 0x226B, 0x226A }, + { 0x226A, 0x226B }, + { 0x22F1, 0x22F0 }, + { 0x22F0, 0x22F1 }, + { 0xFF60, 0xFF5F }, + { 0xFF5F, 0xFF60 }, + { 0xFF63, 0xFF62 }, + { 0xFF62, 0xFF63 }, + + { 0x111111, 0x111111 }, +}; +static const test_pair_t mirroring_tests_more[] = +{ + /* No new mirroring characters have been encoded in recent Unicode versions. */ + { 0x111111, 0x111111 } +}; + +static const test_pair_t script_tests[] = +{ + { 0x002A, HB_SCRIPT_COMMON }, + { 0x0670, HB_SCRIPT_INHERITED }, + { 0x060D, HB_SCRIPT_ARABIC }, + { 0x0559, HB_SCRIPT_ARMENIAN }, + { 0x09CD, HB_SCRIPT_BENGALI }, + { 0x31B6, HB_SCRIPT_BOPOMOFO }, + { 0x13A2, HB_SCRIPT_CHEROKEE }, + { 0x2CFD, HB_SCRIPT_COPTIC }, + { 0x0482, HB_SCRIPT_CYRILLIC }, + { 0x10401, HB_SCRIPT_DESERET }, + { 0x094D, HB_SCRIPT_DEVANAGARI }, + { 0x1258, HB_SCRIPT_ETHIOPIC }, + { 0x10FC, HB_SCRIPT_GEORGIAN }, + { 0x10341, HB_SCRIPT_GOTHIC }, + { 0x0375, HB_SCRIPT_GREEK }, + { 0x0A83, HB_SCRIPT_GUJARATI }, + { 0x0A3C, HB_SCRIPT_GURMUKHI }, + { 0x3005, HB_SCRIPT_HAN }, + { 0x1100, HB_SCRIPT_HANGUL }, + { 0x05BF, HB_SCRIPT_HEBREW }, + { 0x309F, HB_SCRIPT_HIRAGANA }, + { 0x0CBC, HB_SCRIPT_KANNADA }, + { 0x30FF, HB_SCRIPT_KATAKANA }, + { 0x17DD, HB_SCRIPT_KHMER }, + { 0x0EDD, HB_SCRIPT_LAO }, + { 0x0061, HB_SCRIPT_LATIN }, + { 0x0D3D, HB_SCRIPT_MALAYALAM }, + { 0x1843, HB_SCRIPT_MONGOLIAN }, + { 0x1031, HB_SCRIPT_MYANMAR }, + { 0x169C, HB_SCRIPT_OGHAM }, + { 0x10322, HB_SCRIPT_OLD_ITALIC }, + { 0x0B3C, HB_SCRIPT_ORIYA }, + { 0x16EF, HB_SCRIPT_RUNIC }, + { 0x0DBD, HB_SCRIPT_SINHALA }, + { 0x0711, HB_SCRIPT_SYRIAC }, + { 0x0B82, HB_SCRIPT_TAMIL }, + { 0x0C03, HB_SCRIPT_TELUGU }, + { 0x07B1, HB_SCRIPT_THAANA }, + { 0x0E31, HB_SCRIPT_THAI }, + { 0x0FD4, HB_SCRIPT_TIBETAN }, + { 0x1401, HB_SCRIPT_CANADIAN_ABORIGINAL }, + { 0xA015, HB_SCRIPT_YI }, + { 0x1700, HB_SCRIPT_TAGALOG }, + { 0x1720, HB_SCRIPT_HANUNOO }, + { 0x1740, HB_SCRIPT_BUHID }, + { 0x1760, HB_SCRIPT_TAGBANWA }, + + /* Unicode-4.0 additions */ + { 0x2800, HB_SCRIPT_BRAILLE }, + { 0x10808, HB_SCRIPT_CYPRIOT }, + { 0x1932, HB_SCRIPT_LIMBU }, + { 0x10480, HB_SCRIPT_OSMANYA }, + { 0x10450, HB_SCRIPT_SHAVIAN }, + { 0x10000, HB_SCRIPT_LINEAR_B }, + { 0x1950, HB_SCRIPT_TAI_LE }, + { 0x1039F, HB_SCRIPT_UGARITIC }, + + /* Unicode-4.1 additions */ + { 0x1980, HB_SCRIPT_NEW_TAI_LUE }, + { 0x1A1F, HB_SCRIPT_BUGINESE }, + { 0x2C00, HB_SCRIPT_GLAGOLITIC }, + { 0x2D6F, HB_SCRIPT_TIFINAGH }, + { 0xA800, HB_SCRIPT_SYLOTI_NAGRI }, + { 0x103D0, HB_SCRIPT_OLD_PERSIAN }, + { 0x10A3F, HB_SCRIPT_KHAROSHTHI }, + + /* Unicode-5.0 additions */ + { 0x0378, HB_SCRIPT_UNKNOWN }, + { 0x1B04, HB_SCRIPT_BALINESE }, + { 0x12000, HB_SCRIPT_CUNEIFORM }, + { 0x10900, HB_SCRIPT_PHOENICIAN }, + { 0xA840, HB_SCRIPT_PHAGS_PA }, + { 0x07C0, HB_SCRIPT_NKO }, + + /* Unicode-5.1 additions */ + { 0xA900, HB_SCRIPT_KAYAH_LI }, + { 0x1C00, HB_SCRIPT_LEPCHA }, + { 0xA930, HB_SCRIPT_REJANG }, + { 0x1B80, HB_SCRIPT_SUNDANESE }, + { 0xA880, HB_SCRIPT_SAURASHTRA }, + { 0xAA00, HB_SCRIPT_CHAM }, + { 0x1C50, HB_SCRIPT_OL_CHIKI }, + { 0xA500, HB_SCRIPT_VAI }, + { 0x102A0, HB_SCRIPT_CARIAN }, + { 0x10280, HB_SCRIPT_LYCIAN }, + { 0x1093F, HB_SCRIPT_LYDIAN }, + + { 0x111111, HB_SCRIPT_UNKNOWN } +}; +static const test_pair_t script_tests_more[] = +{ + /* Unicode-5.2 additions */ + { 0x10B00, HB_SCRIPT_AVESTAN }, + { 0xA6A0, HB_SCRIPT_BAMUM }, + { 0x13000, HB_SCRIPT_EGYPTIAN_HIEROGLYPHS }, + { 0x10840, HB_SCRIPT_IMPERIAL_ARAMAIC }, + { 0x10B60, HB_SCRIPT_INSCRIPTIONAL_PAHLAVI }, + { 0x10B40, HB_SCRIPT_INSCRIPTIONAL_PARTHIAN }, + { 0xA980, HB_SCRIPT_JAVANESE }, + { 0x11082, HB_SCRIPT_KAITHI }, + { 0xA4D0, HB_SCRIPT_LISU }, + { 0xABE5, HB_SCRIPT_MEETEI_MAYEK }, + { 0x10A60, HB_SCRIPT_OLD_SOUTH_ARABIAN }, + { 0x10C00, HB_SCRIPT_OLD_TURKIC }, + { 0x0800, HB_SCRIPT_SAMARITAN }, + { 0x1A20, HB_SCRIPT_TAI_THAM }, + { 0xAA80, HB_SCRIPT_TAI_VIET }, + + /* Unicode-6.0 additions */ + { 0x1BC0, HB_SCRIPT_BATAK }, + { 0x11000, HB_SCRIPT_BRAHMI }, + { 0x0840, HB_SCRIPT_MANDAIC }, + + /* Unicode-5.2 character additions */ + { 0x1CED, HB_SCRIPT_INHERITED }, + { 0x1400, HB_SCRIPT_CANADIAN_ABORIGINAL }, + + { 0x111111, HB_SCRIPT_UNKNOWN } +}; + + +typedef unsigned int (*get_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); +typedef unsigned int (*func_setter_func_t) (hb_unicode_funcs_t *ufuncs, + get_func_t func, + void *user_data, + hb_destroy_func_t destroy); +typedef unsigned int (*getter_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + +typedef struct { + const char *name; + func_setter_func_t func_setter; + getter_func_t getter; + const test_pair_t *tests; + unsigned int num_tests; + const test_pair_t *tests_more; + unsigned int num_tests_more; + unsigned int default_value; +} property_t; + +#define RETURNS_UNICODE_ITSELF ((unsigned int) -1) + +#define PROPERTY(name, DEFAULT) \ + { \ + #name, \ + (func_setter_func_t) hb_unicode_funcs_set_##name##_func, \ + (getter_func_t) hb_unicode_##name, \ + name##_tests, \ + G_N_ELEMENTS (name##_tests), \ + name##_tests_more, \ + G_N_ELEMENTS (name##_tests_more), \ + DEFAULT \ + } +static const property_t properties[] = +{ + PROPERTY (combining_class, 0), + PROPERTY (eastasian_width, 1), + PROPERTY (general_category, (unsigned int) HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER), + PROPERTY (mirroring, RETURNS_UNICODE_ITSELF), + PROPERTY (script, (unsigned int) HB_SCRIPT_UNKNOWN) +}; +#undef PROPERTY + +static void +test_unicode_properties (gconstpointer user_data) +{ + hb_unicode_funcs_t *uf = (hb_unicode_funcs_t *) user_data; + unsigned int i, j; + gboolean failed = TRUE; + + g_assert (hb_unicode_funcs_is_immutable (uf)); + g_assert (hb_unicode_funcs_get_parent (uf)); + + for (i = 0; i < G_N_ELEMENTS (properties); i++) { + const property_t *p = &properties[i]; + const test_pair_t *tests; + + g_test_message ("Testing property %s", p->name); + tests = p->tests; + for (j = 0; j < p->num_tests; j++) { + g_test_message ("Test %s #%d: U+%04X", p->name, j, tests[j].unicode); + g_assert_cmphex (p->getter (uf, tests[j].unicode), ==, tests[j].value); + } + /* These tests are from Unicode 5.2 onward and older glib/ICU + * don't get them right. Just warn instead of assert. */ + tests = p->tests_more; + for (j = 0; j < p->num_tests_more; j++) { + g_test_message ("Test %s more #%d: U+%04X", p->name, j, tests[j].unicode); + if (p->getter (uf, tests[j].unicode) != tests[j].value) { + g_test_message ("Soft fail: Received %x, expected %x", p->getter (uf, tests[j].unicode), tests[j].value); + failed = TRUE; + } + } + } + + if (failed) + g_test_message ("Some property tests failed. You probably have an old version of one of the libraries used."); +} + +static hb_codepoint_t +default_value (hb_codepoint_t _default_value, hb_codepoint_t unicode) +{ + return _default_value == RETURNS_UNICODE_ITSELF ? unicode : _default_value; +} + +static void +_test_unicode_properties_nil (hb_unicode_funcs_t *uf) +{ + unsigned int i, j; + + for (i = 0; i < G_N_ELEMENTS (properties); i++) { + const property_t *p = &properties[i]; + const test_pair_t *tests; + + g_test_message ("Testing property %s", p->name); + tests = p->tests; + for (j = 0; j < p->num_tests; j++) { + g_test_message ("Test %s #%d: U+%04X", p->name, j, tests[j].unicode); + g_assert_cmphex (p->getter (uf, tests[j].unicode), ==, default_value (p->default_value, tests[j].unicode)); + } + tests = p->tests_more; + for (j = 0; j < p->num_tests_more; j++) { + g_test_message ("Test %s more #%d: U+%04X", p->name, j, tests[j].unicode); + g_assert_cmphex (p->getter (uf, tests[j].unicode), ==, default_value (p->default_value, tests[j].unicode)); + } + } +} + +static void +test_unicode_properties_nil (void) +{ + hb_unicode_funcs_t *uf = hb_unicode_funcs_create (NULL); + + g_assert (!hb_unicode_funcs_is_immutable (uf)); + _test_unicode_properties_nil (uf); + + hb_unicode_funcs_destroy (uf); +} + +static void +test_unicode_properties_empty (void) +{ + hb_unicode_funcs_t *uf = hb_unicode_funcs_get_empty (); + + g_assert (uf); + g_assert (hb_unicode_funcs_is_immutable (uf)); + _test_unicode_properties_nil (uf); +} + + +static void +test_unicode_chainup (void) +{ + hb_unicode_funcs_t *uf, *uf2; + + /* Chain-up to nil */ + + uf = hb_unicode_funcs_create (NULL); + g_assert (!hb_unicode_funcs_is_immutable (uf)); + + uf2 = hb_unicode_funcs_create (uf); + g_assert (hb_unicode_funcs_is_immutable (uf)); + hb_unicode_funcs_destroy (uf); + + g_assert (!hb_unicode_funcs_is_immutable (uf2)); + _test_unicode_properties_nil (uf2); + + hb_unicode_funcs_destroy (uf2); + + /* Chain-up to default */ + + uf = hb_unicode_funcs_create (hb_unicode_funcs_get_default ()); + g_assert (!hb_unicode_funcs_is_immutable (uf)); + + uf2 = hb_unicode_funcs_create (uf); + g_assert (hb_unicode_funcs_is_immutable (uf)); + hb_unicode_funcs_destroy (uf); + + g_assert (!hb_unicode_funcs_is_immutable (uf2)); + hb_unicode_funcs_make_immutable (uf2); + test_unicode_properties (uf2); + + hb_unicode_funcs_destroy (uf2); + +} + +static void +test_unicode_setters (void) +{ + hb_unicode_funcs_t *uf; + unsigned int i; + + /* This is cruel: we use script-returning functions to test all properties, + * but it works. */ + + for (i = 0; i < G_N_ELEMENTS (properties); i++) { + const property_t *p = &properties[i]; + data_t data[2] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}}; + + g_test_message ("Testing property %s", p->name); + + uf = hb_unicode_funcs_create (NULL); + g_assert (!hb_unicode_funcs_is_immutable (uf)); + + p->func_setter (uf, (get_func_t) simple_get_script, &data[0], free_up); + + g_assert_cmphex (p->getter (uf, 'a'), ==, HB_SCRIPT_LATIN); + g_assert_cmphex (p->getter (uf, '0'), ==, HB_SCRIPT_UNKNOWN); + + p->func_setter (uf, (get_func_t) NULL, NULL, NULL); + g_assert (data[0].freed && !data[1].freed); + + g_assert (!hb_unicode_funcs_is_immutable (uf)); + hb_unicode_funcs_make_immutable (uf); + g_assert (hb_unicode_funcs_is_immutable (uf)); + + /* Since uf is immutable now, the following setter should do nothing. */ + p->func_setter (uf, (get_func_t) a_is_for_arabic_get_script, &data[1], free_up); + + g_assert (data[0].freed && !data[1].freed); + hb_unicode_funcs_destroy (uf); + g_assert (data[0].freed && !data[1].freed); + } +} + + + +typedef struct { + data_t data[2]; +} data_fixture_t; + +static void +data_fixture_init (data_fixture_t *f, gconstpointer user_data) +{ + f->data[0].value = MAGIC0; + f->data[1].value = MAGIC1; +} +static void +data_fixture_finish (data_fixture_t *f, gconstpointer user_data) +{ +} + +static void +test_unicode_subclassing_nil (data_fixture_t *f, gconstpointer user_data) +{ + hb_unicode_funcs_t *uf, *aa; + + uf = hb_unicode_funcs_create (NULL); + + aa = hb_unicode_funcs_create (uf); + + hb_unicode_funcs_destroy (uf); + + hb_unicode_funcs_set_script_func (aa, a_is_for_arabic_get_script, + &f->data[1], free_up); + + g_assert_cmphex (hb_unicode_script (aa, 'a'), ==, HB_SCRIPT_ARABIC); + g_assert_cmphex (hb_unicode_script (aa, 'b'), ==, HB_SCRIPT_UNKNOWN); + + g_assert (!f->data[0].freed && !f->data[1].freed); + hb_unicode_funcs_destroy (aa); + g_assert (!f->data[0].freed && f->data[1].freed); +} + +static void +test_unicode_subclassing_default (data_fixture_t *f, gconstpointer user_data) +{ + hb_unicode_funcs_t *uf, *aa; + + uf = hb_unicode_funcs_get_default (); + aa = hb_unicode_funcs_create (uf); + + hb_unicode_funcs_set_script_func (aa, a_is_for_arabic_get_script, + &f->data[1], free_up); + + g_assert_cmphex (hb_unicode_script (aa, 'a'), ==, HB_SCRIPT_ARABIC); + g_assert_cmphex (hb_unicode_script (aa, 'b'), ==, HB_SCRIPT_LATIN); + + g_assert (!f->data[0].freed && !f->data[1].freed); + hb_unicode_funcs_destroy (aa); + g_assert (!f->data[0].freed && f->data[1].freed); +} + +static void +test_unicode_subclassing_deep (data_fixture_t *f, gconstpointer user_data) +{ + hb_unicode_funcs_t *uf, *aa; + + uf = hb_unicode_funcs_create (NULL); + + hb_unicode_funcs_set_script_func (uf, simple_get_script, + &f->data[0], free_up); + + aa = hb_unicode_funcs_create (uf); + + hb_unicode_funcs_destroy (uf); + + /* make sure the 'uf' didn't get freed, since 'aa' holds a ref */ + g_assert (!f->data[0].freed); + + hb_unicode_funcs_set_script_func (aa, a_is_for_arabic_get_script, + &f->data[1], free_up); + + g_assert_cmphex (hb_unicode_script (aa, 'a'), ==, HB_SCRIPT_ARABIC); + g_assert_cmphex (hb_unicode_script (aa, 'b'), ==, HB_SCRIPT_LATIN); + g_assert_cmphex (hb_unicode_script (aa, '0'), ==, HB_SCRIPT_UNKNOWN); + + g_assert (!f->data[0].freed && !f->data[1].freed); + hb_unicode_funcs_destroy (aa); + g_assert (f->data[0].freed && f->data[1].freed); +} + + +static hb_script_t +script_roundtrip_default (hb_script_t script) +{ + return hb_script_from_iso15924_tag (hb_script_to_iso15924_tag (script)); +} + +#ifdef HAVE_GLIB +static hb_script_t +script_roundtrip_glib (hb_script_t script) +{ + return hb_glib_script_to_script (hb_glib_script_from_script (script)); +} +#endif + +#ifdef HAVE_ICU +static hb_script_t +script_roundtrip_icu (hb_script_t script) +{ + return hb_icu_script_to_script (hb_icu_script_from_script (script)); +} +#endif + +static void +test_unicode_script_roundtrip (gconstpointer user_data) +{ + typedef hb_script_t (*roundtrip_func_t) (hb_script_t); + roundtrip_func_t roundtrip_func = (roundtrip_func_t) user_data; + unsigned int i; + gboolean failed = FALSE; + + for (i = 0; i < G_N_ELEMENTS (script_tests); i++) { + const test_pair_t *test = &script_tests[i]; + hb_script_t script = test->value; + + g_test_message ("Test script roundtrip #%d: %x", i, script); + g_assert_cmphex (script, ==, roundtrip_func (script)); + } + for (i = 0; i < G_N_ELEMENTS (script_tests_more); i++) { + const test_pair_t *test = &script_tests_more[i]; + hb_script_t script = test->value; + + g_test_message ("Test script roundtrip more #%d: %x", i, script); + if (script != roundtrip_func (script)) { + g_test_message ("Soft fail: Received %x, expected %x", roundtrip_func (script), script); + failed = TRUE; + } + } + + g_assert_cmphex (HB_SCRIPT_INVALID, ==, roundtrip_func (HB_SCRIPT_INVALID)); + + if (failed) + g_test_message ("Some script roundtrip tests failed. You probably have an old version of one of the libraries used."); +} + + +static void +test_unicode_normalization (gconstpointer user_data) +{ + hb_unicode_funcs_t *uf = (hb_unicode_funcs_t *) user_data; + gunichar a, b, ab; + + + /* Test compose() */ + + /* Not composable */ + g_assert (!hb_unicode_compose (uf, 0x0041, 0x0042, &ab) && ab == 0); + g_assert (!hb_unicode_compose (uf, 0x0041, 0, &ab) && ab == 0); + g_assert (!hb_unicode_compose (uf, 0x0066, 0x0069, &ab) && ab == 0); + + /* Singletons should not compose */ + g_assert (!hb_unicode_compose (uf, 0x212B, 0, &ab) && ab == 0); + g_assert (!hb_unicode_compose (uf, 0x00C5, 0, &ab) && ab == 0); + g_assert (!hb_unicode_compose (uf, 0x2126, 0, &ab) && ab == 0); + g_assert (!hb_unicode_compose (uf, 0x03A9, 0, &ab) && ab == 0); + + /* Non-starter pairs should not compose */ + g_assert (!hb_unicode_compose (uf, 0x0308, 0x0301, &ab) && ab == 0); /* !0x0344 */ + g_assert (!hb_unicode_compose (uf, 0x0F71, 0x0F72, &ab) && ab == 0); /* !0x0F73 */ + + /* Pairs */ + g_assert (hb_unicode_compose (uf, 0x0041, 0x030A, &ab) && ab == 0x00C5); + g_assert (hb_unicode_compose (uf, 0x006F, 0x0302, &ab) && ab == 0x00F4); + g_assert (hb_unicode_compose (uf, 0x1E63, 0x0307, &ab) && ab == 0x1E69); + g_assert (hb_unicode_compose (uf, 0x0073, 0x0323, &ab) && ab == 0x1E63); + g_assert (hb_unicode_compose (uf, 0x0064, 0x0307, &ab) && ab == 0x1E0B); + g_assert (hb_unicode_compose (uf, 0x0064, 0x0323, &ab) && ab == 0x1E0D); + + /* Hangul */ + g_assert (hb_unicode_compose (uf, 0xD4CC, 0x11B6, &ab) && ab == 0xD4DB); + g_assert (hb_unicode_compose (uf, 0x1111, 0x1171, &ab) && ab == 0xD4CC); + g_assert (hb_unicode_compose (uf, 0xCE20, 0x11B8, &ab) && ab == 0xCE31); + g_assert (hb_unicode_compose (uf, 0x110E, 0x1173, &ab) && ab == 0xCE20); + + + /* Test decompose() */ + + /* Not decomposable */ + g_assert (!hb_unicode_decompose (uf, 0x0041, &a, &b) && a == 0x0041 && b == 0); + g_assert (!hb_unicode_decompose (uf, 0xFB01, &a, &b) && a == 0xFB01 && b == 0); + g_assert (!hb_unicode_decompose (uf, 0x1F1EF, &a, &b) && a == 0x1F1EF && b == 0); + + /* Singletons */ + g_assert (hb_unicode_decompose (uf, 0x212B, &a, &b) && a == 0x00C5 && b == 0); + g_assert (hb_unicode_decompose (uf, 0x2126, &a, &b) && a == 0x03A9 && b == 0); + + /* Non-starter pairs decompose, but not compose */ + g_assert (hb_unicode_decompose (uf, 0x0344, &a, &b) && a == 0x0308 && b == 0x0301); + g_assert (hb_unicode_decompose (uf, 0x0F73, &a, &b) && a == 0x0F71 && b == 0x0F72); + + /* Pairs */ + g_assert (hb_unicode_decompose (uf, 0x00C5, &a, &b) && a == 0x0041 && b == 0x030A); + g_assert (hb_unicode_decompose (uf, 0x00F4, &a, &b) && a == 0x006F && b == 0x0302); + g_assert (hb_unicode_decompose (uf, 0x1E69, &a, &b) && a == 0x1E63 && b == 0x0307); + g_assert (hb_unicode_decompose (uf, 0x1E63, &a, &b) && a == 0x0073 && b == 0x0323); + g_assert (hb_unicode_decompose (uf, 0x1E0B, &a, &b) && a == 0x0064 && b == 0x0307); + g_assert (hb_unicode_decompose (uf, 0x1E0D, &a, &b) && a == 0x0064 && b == 0x0323); + + /* Hangul */ + g_assert (hb_unicode_decompose (uf, 0xD4DB, &a, &b) && a == 0xD4CC && b == 0x11B6); + g_assert (hb_unicode_decompose (uf, 0xD4CC, &a, &b) && a == 0x1111 && b == 0x1171); + g_assert (hb_unicode_decompose (uf, 0xCE31, &a, &b) && a == 0xCE20 && b == 0x11B8); + g_assert (hb_unicode_decompose (uf, 0xCE20, &a, &b) && a == 0x110E && b == 0x1173); + +} + + + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_unicode_properties_nil); + hb_test_add (test_unicode_properties_empty); + + hb_test_add_data_flavor (hb_unicode_funcs_get_default (), "default", test_unicode_properties); + hb_test_add_data_flavor (hb_unicode_funcs_get_default (), "default", test_unicode_normalization); + hb_test_add_data_flavor ((gconstpointer) script_roundtrip_default, "default", test_unicode_script_roundtrip); +#ifdef HAVE_GLIB + hb_test_add_data_flavor (hb_glib_get_unicode_funcs (), "glib", test_unicode_properties); + hb_test_add_data_flavor (hb_glib_get_unicode_funcs (), "glib", test_unicode_normalization); + hb_test_add_data_flavor ((gconstpointer) script_roundtrip_glib, "glib", test_unicode_script_roundtrip); +#endif +#ifdef HAVE_ICU + hb_test_add_data_flavor (hb_icu_get_unicode_funcs (), "icu", test_unicode_properties); + hb_test_add_data_flavor (hb_icu_get_unicode_funcs (), "icu", test_unicode_normalization); + hb_test_add_data_flavor ((gconstpointer) script_roundtrip_icu, "icu", test_unicode_script_roundtrip); +#endif + + hb_test_add (test_unicode_chainup); + + hb_test_add (test_unicode_setters); + + hb_test_add_fixture (data_fixture, NULL, test_unicode_subclassing_nil); + hb_test_add_fixture (data_fixture, NULL, test_unicode_subclassing_default); + hb_test_add_fixture (data_fixture, NULL, test_unicode_subclassing_deep); + + return hb_test_run (); +}
diff --git a/test/api/test-version.c b/test/api/test-version.c new file mode 100644 index 0000000..4c9bd37 --- /dev/null +++ b/test/api/test-version.c
@@ -0,0 +1,80 @@ +/* + * 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-version.h */ + + +static void +test_version (void) +{ + unsigned int major, minor, micro; + char *s; + + hb_version (&major, &minor, µ); + + g_assert_cmpint (major, ==, HB_VERSION_MAJOR); + g_assert_cmpint (minor, ==, HB_VERSION_MINOR); + g_assert_cmpint (micro, ==, HB_VERSION_MICRO); + + s = g_strdup_printf ("%u.%u.%u", major, minor, micro); + g_assert (0 == strcmp (HB_VERSION_STRING, s)); + g_free (s); + g_assert (0 == strcmp (HB_VERSION_STRING, hb_version_string ())); + + g_assert (HB_VERSION_CHECK (major, minor, micro)); + g_assert (HB_VERSION_CHECK (major+1, minor, micro)); + g_assert (HB_VERSION_CHECK (major, minor+1, micro)); + g_assert (HB_VERSION_CHECK (major, minor, micro+1)); + if (major) + g_assert (!HB_VERSION_CHECK (major-1, minor, micro)); + if (minor) + g_assert (!HB_VERSION_CHECK (major, minor-1, micro)); + if (micro) + g_assert (!HB_VERSION_CHECK (major, minor, micro-1)); + + g_assert (hb_version_check (major, minor, micro)); + g_assert (hb_version_check (major+1, minor, micro)); + g_assert (hb_version_check (major, minor+1, micro)); + g_assert (hb_version_check (major, minor, micro+1)); + if (major) + g_assert (!hb_version_check (major-1, minor, micro)); + if (minor) + g_assert (!hb_version_check (major, minor-1, micro)); + if (micro) + g_assert (!hb_version_check (major, minor, micro-1)); +} + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_version); + + return hb_test_run(); +}