summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/INDEX8
-rw-r--r--test/Makefile14
-rw-r--r--test/data/oldfminit/Allerta (renamed from test/data/Encoding)0
-rw-r--r--test/data/oldfminit/INDEX10
-rw-r--r--test/data/oldfminit/Latin1270
-rw-r--r--test/data/oldfminit/brokenencoding.cfg28
-rw-r--r--test/data/oldfminit/latin1.cfg28
-rw-r--r--test/data/oldfminit/mergeumap.cfg40
-rw-r--r--test/data/oldfminit/nomapping.cfg19
-rw-r--r--test/data/oldfminit/symbol.cfg28
-rw-r--r--test/harness-priv.h45
-rw-r--r--test/harness.c139
-rw-r--r--test/harness.h10
-rw-r--r--test/manyfonts.c100
-rw-r--r--test/mocks.c671
-rw-r--r--test/nofonts.c20
-rw-r--r--test/oldfminit.c353
-rw-r--r--test/olducsinit.c140
-rw-r--r--test/rufl_chars.c68
-rw-r--r--test/rufl_test.c21
-rw-r--r--test/testutils.h123
-rw-r--r--test/ucsinit.c140
22 files changed, 2243 insertions, 32 deletions
diff --git a/test/INDEX b/test/INDEX
new file mode 100644
index 0000000..417b032
--- /dev/null
+++ b/test/INDEX
@@ -0,0 +1,8 @@
+# Index for testcases
+#
+# Test Description DataDir
+nofonts Ensure a lack of fonts "works"
+ucsinit Ensure that UCS FM initialisation works
+olducsinit Ensure that UCS FM (pre 3.64) initialisation works
+oldfminit Ensure that non-UCS FM initialisation works oldfminit
+manyfonts Ensure that more than 256 fonts works
diff --git a/test/Makefile b/test/Makefile
index 2bfa58b..65c3170 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,4 +1,16 @@
# Tests
-DIR_TEST_ITEMS := rufl_test:rufl_test.c rufl_chars:rufl_chars.c
+ifeq ($(findstring -riscos,$(HOST)),-riscos)
+ DIR_TEST_ITEMS := $(DIR_TEST_ITEMS) \
+ rufl_test:rufl_test.c \
+ rufl_chars:rufl_chars.c
+ # We do not want to run tests if building for RISC OS
+ TESTRUNNER := echo
+else
+ DIR_TEST_ITEMS := nofonts:nofonts.c;harness.c;mocks.c \
+ oldfminit:oldfminit.c;harness.c;mocks.c \
+ olducsinit:olducsinit.c;harness.c;mocks.c \
+ ucsinit:ucsinit.c;harness.c;mocks.c \
+ manyfonts:manyfonts.c;harness.c;mocks.c
+endif
include $(NSBUILD)/Makefile.subdir
diff --git a/test/data/Encoding b/test/data/oldfminit/Allerta
index e5b633e..e5b633e 100644
--- a/test/data/Encoding
+++ b/test/data/oldfminit/Allerta
diff --git a/test/data/oldfminit/INDEX b/test/data/oldfminit/INDEX
new file mode 100644
index 0000000..c2d250f
--- /dev/null
+++ b/test/data/oldfminit/INDEX
@@ -0,0 +1,10 @@
+# Index file for non-UCS FM initialisation tests
+#
+# Test Description
+
+latin1.cfg Simple Latin1 Encoding
+mergeumap.cfg Merge identical umaps
+nomapping.cfg Fonts with no mapping
+symbol.cfg Simple symbol fonts
+
+brokenencoding.cfg Garbage encoding file
diff --git a/test/data/oldfminit/Latin1 b/test/data/oldfminit/Latin1
new file mode 100644
index 0000000..6821aa8
--- /dev/null
+++ b/test/data/oldfminit/Latin1
@@ -0,0 +1,270 @@
+% Acorn_Latin1Encoding 1.00 0
+
+%%RISCOS_BasedOn 0
+%%RISCOS_Alphabet 101
+
+% These first characters are for use by PostScript printer driver ONLY,
+% they are not accessible using the RISC OS font manager.
+/ring
+/circumflex
+/tilde
+/dotlessi
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+/.notdef
+
+/space
+/exclam
+/quotedbl
+/numbersign
+/dollar
+/percent
+/ampersand
+/quotesingle
+/parenleft
+/parenright
+/asterisk
+/plus
+/comma
+/hyphen
+/period
+/slash
+/zero
+/one
+/two
+/three
+/four
+/five
+/six
+/seven
+/eight
+/nine
+/colon
+/semicolon
+/less
+/equal
+/greater
+/question
+
+/at
+/A
+/B
+/C
+/D
+/E
+/F
+/G
+/H
+/I
+/J
+/K
+/L
+/M
+/N
+/O
+/P
+/Q
+/R
+/S
+/T
+/U
+/V
+/W
+/X
+/Y
+/Z
+/bracketleft
+/backslash
+/bracketright
+/asciicircum
+/underscore
+
+/grave
+/a
+/b
+/c
+/d
+/e
+/f
+/g
+/h
+/i
+/j
+/k
+/l
+/m
+/n
+/o
+/p
+/q
+/r
+/s
+/t
+/u
+/v
+/w
+/x
+/y
+/z
+/braceleft
+/bar
+/braceright
+/asciitilde
+/.notdef
+
+/Euro
+/Wcircumflex
+/wcircumflex
+/.notdef
+/.notdef
+/Ycircumflex
+/ycircumflex
+/special1
+/special2
+/special3
+/special4
+/special5
+/ellipsis
+/trademark
+/perthousand
+/bullet
+/quoteleft
+/quoteright
+/guilsinglleft
+/guilsinglright
+/quotedblleft
+/quotedblright
+/quotedblbase
+/endash
+/emdash
+/minus
+/OE
+/oe
+/dagger
+/daggerdbl
+/fi
+/fl
+
+/space
+/exclamdown
+/cent
+/sterling
+/currency
+/yen
+/brokenbar
+/section
+/dieresis
+/copyright
+/ordfeminine
+/guillemotleft
+/logicalnot
+/hyphen
+/registered
+/macron
+/degree
+/plusminus
+/twosuperior
+/threesuperior
+/acute
+/mu
+/paragraph
+/periodcentered
+/cedilla
+/onesuperior
+/ordmasculine
+/guillemotright
+/onequarter
+/onehalf
+/threequarters
+/questiondown
+
+/Agrave
+/Aacute
+/Acircumflex
+/Atilde
+/Adieresis
+/Aring
+/AE
+/Ccedilla
+/Egrave
+/Eacute
+/Ecircumflex
+/Edieresis
+/Igrave
+/Iacute
+/Icircumflex
+/Idieresis
+/Eth
+/Ntilde
+/Ograve
+/Oacute
+/Ocircumflex
+/Otilde
+/Odieresis
+/multiply
+/Oslash
+/Ugrave
+/Uacute
+/Ucircumflex
+/Udieresis
+/Yacute
+/Thorn
+/germandbls
+
+/agrave
+/aacute
+/acircumflex
+/atilde
+/adieresis
+/aring
+/ae
+/ccedilla
+/egrave
+/eacute
+/ecircumflex
+/edieresis
+/igrave
+/iacute
+/icircumflex
+/idieresis
+/eth
+/ntilde
+/ograve
+/oacute
+/ocircumflex
+/otilde
+/odieresis
+/divide
+/oslash
+/ugrave
+/uacute
+/ucircumflex
+/udieresis
+/yacute
+/thorn
+/ydieresis
diff --git a/test/data/oldfminit/brokenencoding.cfg b/test/data/oldfminit/brokenencoding.cfg
new file mode 100644
index 0000000..b5e12c1
--- /dev/null
+++ b/test/data/oldfminit/brokenencoding.cfg
@@ -0,0 +1,28 @@
+# Configuration for broken encoding file
+
+%expumaps Corpus.Bold 0
+%expumaps Corpus.Bold.Oblique 0
+%expumaps Corpus.Medium 1
+%expumaps Corpus.Medium.Oblique 0
+%expumaps Homerton.Bold 0
+%expumaps Homerton.Bold.Oblique 0
+%expumaps Homerton.Medium 1
+%expumaps Homerton.Medium.Oblique 0
+%expumaps Trinity.Bold 0
+%expumaps Trinity.Bold.Italic 0
+%expumaps Trinity.Medium 1
+%expumaps Trinity.Medium.Italic 0
+
+# Font name Encoding name Filename
+Corpus.Bold Latin1 Allerta
+Corpus.Bold.Oblique Latin1 Allerta
+Corpus.Medium Latin1 Latin1
+Corpus.Medium.Oblique Latin1 Allerta
+Homerton.Bold Latin1 Allerta
+Homerton.Bold.Oblique Latin1 Allerta
+Homerton.Medium Latin1 Latin1
+Homerton.Medium.Oblique Latin1 Allerta
+Trinity.Bold Latin1 Allerta
+Trinity.Bold.Italic Latin1 Allerta
+Trinity.Medium Latin1 Latin1
+Trinity.Medium.Italic Latin1 Allerta
diff --git a/test/data/oldfminit/latin1.cfg b/test/data/oldfminit/latin1.cfg
new file mode 100644
index 0000000..646582a
--- /dev/null
+++ b/test/data/oldfminit/latin1.cfg
@@ -0,0 +1,28 @@
+# Configuration for Latin1 language fonts
+
+%expumaps Corpus.Bold 1
+%expumaps Corpus.Bold.Oblique 1
+%expumaps Corpus.Medium 1
+%expumaps Corpus.Medium.Oblique 1
+%expumaps Homerton.Bold 1
+%expumaps Homerton.Bold.Oblique 1
+%expumaps Homerton.Medium 1
+%expumaps Homerton.Medium.Oblique 1
+%expumaps Trinity.Bold 1
+%expumaps Trinity.Bold.Italic 1
+%expumaps Trinity.Medium 1
+%expumaps Trinity.Medium.Italic 1
+
+# Font name Encoding name Filename
+Corpus.Bold Latin1 Latin1
+Corpus.Bold.Oblique Latin1 Latin1
+Corpus.Medium Latin1 Latin1
+Corpus.Medium.Oblique Latin1 Latin1
+Homerton.Bold Latin1 Latin1
+Homerton.Bold.Oblique Latin1 Latin1
+Homerton.Medium Latin1 Latin1
+Homerton.Medium.Oblique Latin1 Latin1
+Trinity.Bold Latin1 Latin1
+Trinity.Bold.Italic Latin1 Latin1
+Trinity.Medium Latin1 Latin1
+Trinity.Medium.Italic Latin1 Latin1
diff --git a/test/data/oldfminit/mergeumap.cfg b/test/data/oldfminit/mergeumap.cfg
new file mode 100644
index 0000000..654a0bf
--- /dev/null
+++ b/test/data/oldfminit/mergeumap.cfg
@@ -0,0 +1,40 @@
+# Configuration for merging duplicate umaps
+
+%expumaps Corpus.Bold 1
+%expumaps Corpus.Bold.Oblique 1
+%expumaps Corpus.Medium 1
+%expumaps Corpus.Medium.Oblique 1
+%expumaps Homerton.Bold 1
+%expumaps Homerton.Bold.Oblique 1
+%expumaps Homerton.Medium 1
+%expumaps Homerton.Medium.Oblique 1
+%expumaps Trinity.Bold 1
+%expumaps Trinity.Bold.Italic 1
+%expumaps Trinity.Medium 1
+%expumaps Trinity.Medium.Italic 1
+
+# Font name Encoding name Filename
+Corpus.Bold Latin1 Latin1
+Corpus.Bold.Oblique Latin1 Latin1
+Corpus.Medium Latin1 Latin1
+Corpus.Medium.Oblique Latin1 Latin1
+Corpus.Bold Latin2 Latin1
+Corpus.Bold.Oblique Latin2 Latin1
+Corpus.Medium Latin2 Latin1
+Corpus.Medium.Oblique Latin2 Latin1
+Homerton.Bold Latin1 Latin1
+Homerton.Bold.Oblique Latin1 Latin1
+Homerton.Medium Latin1 Latin1
+Homerton.Medium.Oblique Latin1 Latin1
+Homerton.Bold Latin2 Latin1
+Homerton.Bold.Oblique Latin2 Latin1
+Homerton.Medium Latin2 Latin1
+Homerton.Medium.Oblique Latin2 Latin1
+Trinity.Bold Latin1 Latin1
+Trinity.Bold.Italic Latin1 Latin1
+Trinity.Medium Latin1 Latin1
+Trinity.Medium.Italic Latin1 Latin1
+Trinity.Bold Latin2 Latin1
+Trinity.Bold.Italic Latin2 Latin1
+Trinity.Medium Latin2 Latin1
+Trinity.Medium.Italic Latin2 Latin1
diff --git a/test/data/oldfminit/nomapping.cfg b/test/data/oldfminit/nomapping.cfg
new file mode 100644
index 0000000..18de2fb
--- /dev/null
+++ b/test/data/oldfminit/nomapping.cfg
@@ -0,0 +1,19 @@
+# Configuration for fonts with no mapping
+
+%expumaps Corpus.Bold 0
+%expumaps Corpus.Bold.Oblique 0
+%expumaps Corpus.Medium 1
+%expumaps Corpus.Medium.Oblique 0
+%expumaps Homerton.Bold 0
+%expumaps Homerton.Bold.Oblique 0
+%expumaps Homerton.Medium 1
+%expumaps Homerton.Medium.Oblique 0
+%expumaps Trinity.Bold 0
+%expumaps Trinity.Bold.Italic 0
+%expumaps Trinity.Medium 1
+%expumaps Trinity.Medium.Italic 0
+
+# Font name Encoding name Filename
+Corpus.Medium Latin1 Latin1
+Homerton.Medium Latin1 Latin1
+Trinity.Medium Latin1 Latin1
diff --git a/test/data/oldfminit/symbol.cfg b/test/data/oldfminit/symbol.cfg
new file mode 100644
index 0000000..ca7c760
--- /dev/null
+++ b/test/data/oldfminit/symbol.cfg
@@ -0,0 +1,28 @@
+# Configuration for symbol fonts
+
+%expumaps Corpus.Bold 1
+%expumaps Corpus.Bold.Oblique 1
+%expumaps Corpus.Medium 1
+%expumaps Corpus.Medium.Oblique 1
+%expumaps Homerton.Bold 1
+%expumaps Homerton.Bold.Oblique 1
+%expumaps Homerton.Medium 1
+%expumaps Homerton.Medium.Oblique 1
+%expumaps Trinity.Bold 1
+%expumaps Trinity.Bold.Italic 1
+%expumaps Trinity.Medium 1
+%expumaps Trinity.Medium.Italic 1
+
+# Font name Encoding name Filename
+Corpus.Bold Symbol Latin1
+Corpus.Bold.Oblique Symbol Latin1
+Corpus.Medium Symbol Latin1
+Corpus.Medium.Oblique Symbol Latin1
+Homerton.Bold Symbol Latin1
+Homerton.Bold.Oblique Symbol Latin1
+Homerton.Medium Symbol Latin1
+Homerton.Medium.Oblique Symbol Latin1
+Trinity.Bold Symbol Latin1
+Trinity.Bold.Italic Symbol Latin1
+Trinity.Medium Symbol Latin1
+Trinity.Medium.Italic Symbol Latin1
diff --git a/test/harness-priv.h b/test/harness-priv.h
new file mode 100644
index 0000000..60d5135
--- /dev/null
+++ b/test/harness-priv.h
@@ -0,0 +1,45 @@
+#ifndef rufl_test_harness_priv_h_
+#define rufl_test_harness_priv_h_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <inttypes.h>
+
+#include "harness.h"
+
+typedef struct {
+ unsigned int refcnt;
+ size_t name; /* Index of name in names array */
+#define FONT_ENCODING_SYMBOL ((size_t) -1) /* Symbol, not language, font */
+ size_t encoding; /* Index of encoding in encodings array */
+ int xsize; /* XSize of this font */
+ int ysize; /* YSize if this font */
+ int xres; /* XResolution of this font */
+ int yres; /* YResolution of this font */
+} rufl_test_harness_sized_font;
+
+typedef struct {
+ int fm_version;
+ bool fm_ucs;
+ bool fm_broken_fec;
+
+ const char **font_names;
+ size_t n_font_names;
+
+ const char **encodings;
+ size_t n_encodings;
+
+ /* n_font_names * (n_encodings + 1) entries */
+ char **encoding_filenames;
+
+ /* At most 256 active font handles */
+ rufl_test_harness_sized_font fonts[256];
+ int current_font;
+
+ char *buffer;
+ int buffer_flags;
+} rufl_test_harness_t;
+
+extern rufl_test_harness_t *h;
+
+#endif
diff --git a/test/harness.c b/test/harness.c
new file mode 100644
index 0000000..8f8ea9a
--- /dev/null
+++ b/test/harness.c
@@ -0,0 +1,139 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "harness-priv.h"
+
+rufl_test_harness_t *h = NULL;
+
+static void rufl_test_harness_free(void)
+{
+ size_t ni, ei;
+
+ free(h->font_names);
+ free(h->encodings);
+ if (h->encoding_filenames != NULL) {
+ for (ni = 0; ni != h->n_font_names; ni++) {
+ for (ei = 0; ei != h->n_encodings + 1; ei++) {
+ free(h->encoding_filenames[
+ (ni * (h->n_encodings + 1)) + ei]);
+ }
+ }
+ }
+ free(h->encoding_filenames);
+ free(h);
+}
+
+void rufl_test_harness_init(int fm_version, bool fm_ucs, bool preload)
+{
+ h = calloc(1, sizeof(*h));
+ assert(h != NULL);
+
+ h->fm_version = fm_version;
+ h->fm_ucs = fm_ucs;
+ h->fm_broken_fec = fm_version < 364;
+
+ if (preload) {
+ /* Register ROM fonts as a convenience */
+ rufl_test_harness_register_font("Corpus.Bold");
+ rufl_test_harness_register_font("Corpus.Bold.Oblique");
+ rufl_test_harness_register_font("Corpus.Medium");
+ rufl_test_harness_register_font("Corpus.Medium.Oblique");
+ rufl_test_harness_register_font("Homerton.Bold");
+ rufl_test_harness_register_font("Homerton.Bold.Oblique");
+ rufl_test_harness_register_font("Homerton.Medium");
+ rufl_test_harness_register_font("Homerton.Medium.Oblique");
+ rufl_test_harness_register_font("Trinity.Bold");
+ rufl_test_harness_register_font("Trinity.Bold.Italic");
+ rufl_test_harness_register_font("Trinity.Medium");
+ rufl_test_harness_register_font("Trinity.Medium.Italic");
+
+ /* Register encodings as a convenience */
+ rufl_test_harness_register_encoding("Cyrillic");
+ rufl_test_harness_register_encoding("Greek");
+ rufl_test_harness_register_encoding("Hebrew");
+ rufl_test_harness_register_encoding("Latin1");
+ rufl_test_harness_register_encoding("Latin2");
+ rufl_test_harness_register_encoding("Latin3");
+ rufl_test_harness_register_encoding("Latin4");
+ rufl_test_harness_register_encoding("Latin5");
+ rufl_test_harness_register_encoding("Latin6");
+ rufl_test_harness_register_encoding("Latin7");
+ rufl_test_harness_register_encoding("Latin8");
+ rufl_test_harness_register_encoding("Latin9");
+ rufl_test_harness_register_encoding("Latin10");
+ if (fm_ucs)
+ rufl_test_harness_register_encoding("UTF8");
+ rufl_test_harness_register_encoding("Welsh");
+ }
+
+ atexit(rufl_test_harness_free);
+}
+
+void rufl_test_harness_register_font(const char *name)
+{
+ const char **names;
+
+ /* Encoding paths must be registered last */
+ assert(h->encoding_filenames == NULL);
+
+ names = realloc(h->font_names,
+ (h->n_font_names + 1) * sizeof(*names));
+ assert(names != NULL);
+
+ h->font_names = names;
+
+ h->font_names[h->n_font_names++] = name;
+}
+
+void rufl_test_harness_register_encoding(const char *encoding)
+{
+ const char **encodings;
+
+ /* Encoding paths must be registered last */
+ assert(h->encoding_filenames == NULL);
+
+ encodings = realloc(h->encodings,
+ (h->n_encodings + 1) * sizeof(*encodings));
+ assert(encodings != NULL);
+
+ h->encodings = encodings;
+
+ h->encodings[h->n_encodings++] = encoding;
+}
+
+void rufl_test_harness_set_font_encoding(const char *fontname,
+ const char *encoding, const char *path)
+{
+ size_t ni, ei;
+
+ if (h->encoding_filenames == NULL) {
+ h->encoding_filenames = calloc(
+ h->n_font_names * (h->n_encodings + 1),
+ sizeof(*h->encoding_filenames));
+ assert(h->encoding_filenames != NULL);
+ }
+
+ /* Find font index */
+ for (ni = 0; ni < h->n_font_names; ni++) {
+ if (strcmp(h->font_names[ni], fontname) == 0)
+ break;
+ }
+ assert(ni != h->n_font_names);
+
+ /* Find encoding index */
+ if (strcmp("Symbol", encoding) == 0) {
+ ei = h->n_encodings;
+ } else {
+ for (ei = 0; ei < h->n_encodings; ei++) {
+ if (strcmp(h->encodings[ei], encoding) == 0)
+ break;
+ }
+ assert(ei != h->n_encodings);
+ }
+
+ if (h->encoding_filenames[(ni * (h->n_encodings + 1)) + ei] != NULL)
+ free(h->encoding_filenames[(ni * (h->n_encodings + 1)) + ei]);
+ h->encoding_filenames[(ni * (h->n_encodings + 1)) + ei] = strdup(path);
+ assert(h->encoding_filenames[(ni * (h->n_encodings + 1)) + ei] != NULL);
+}
diff --git a/test/harness.h b/test/harness.h
new file mode 100644
index 0000000..b62617f
--- /dev/null
+++ b/test/harness.h
@@ -0,0 +1,10 @@
+#ifndef rufl_test_harness_h_
+#define rufl_test_harness_h_
+
+void rufl_test_harness_init(int fm_version, bool fm_ucs, bool preload);
+void rufl_test_harness_register_font(const char *name);
+void rufl_test_harness_register_encoding(const char *encoding);
+void rufl_test_harness_set_font_encoding(const char *fontname,
+ const char *encoding, const char *path);
+
+#endif
diff --git a/test/manyfonts.c b/test/manyfonts.c
new file mode 100644
index 0000000..bc4b01e
--- /dev/null
+++ b/test/manyfonts.c
@@ -0,0 +1,100 @@
+/*
+ * XXX: This test currently needs the following patch to be valid.
+ * We need a way of ensuring the library chooses the direct substitution
+ * table format (which basically means we need to flood a plane with glyphs)
+ *
+ * diff --git a/src/rufl_substitution_table.c b/src/rufl_substitution_table.c
+ * index f5de7d8..2b58b72 100644
+ * --- a/src/rufl_substitution_table.c
+ * +++ b/src/rufl_substitution_table.c
+ * @@ -1019,7 +1019,7 @@ static rufl_code create_substitution_table_for_plane(unsigned int plane)
+ * table_entries, blocks_used);
+ * chd_size = rufl_substitution_table_estimate_size_chd(
+ * table_entries, blocks_used);
+ * - if (direct_size <= chd_size) {
+ * + if (1 || direct_size <= chd_size) {
+ * result = direct(table, table_entries, blocks_used,
+ * block_histogram,
+ * &rufl_substitution_table[plane]);
+ */
+
+#include <ftw.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "rufl.h"
+
+#include "harness.h"
+#include "testutils.h"
+
+static char template[] = "/tmp/manyfontsXXXXXX";
+static const char *ptmp = NULL;
+
+static int ftw_cb(const char *path, const struct stat *sb,
+ int typeflag, struct FTW *ftwbuf)
+{
+ (void) sb;
+ (void) typeflag;
+ (void) ftwbuf;
+
+ remove(path);
+
+ return 0;
+}
+
+static void cleanup(void)
+{
+ if (ptmp == NULL)
+ return;
+
+ nftw(ptmp, ftw_cb, FOPEN_MAX, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+}
+
+int main(int argc, const char **argv)
+{
+ char *names[300];
+ int x;
+
+ UNUSED(argc);
+ UNUSED(argv);
+
+ ptmp = mkdtemp(template);
+ assert(NULL != ptmp);
+ atexit(cleanup);
+ chdir(ptmp);
+
+ rufl_test_harness_init(380, true, true);
+
+ for (x = 0; x < 300; x++) {
+ char buf[64];
+ sprintf(buf, "Font%03d", x);
+ names[x] = strdup(buf);
+ rufl_test_harness_register_font(names[x]);
+ }
+
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(303 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+
+ rufl_dump_state(true);
+
+ rufl_quit();
+
+ /* Reinit -- should load cache */
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(303 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+ /* Done for real this time */
+ rufl_quit();
+
+ for (x = 0; x < 300; x++) {
+ free(names[x]);
+ }
+
+ printf("PASS\n");
+
+ return 0;
+}
diff --git a/test/mocks.c b/test/mocks.c
new file mode 100644
index 0000000..734211b
--- /dev/null
+++ b/test/mocks.c
@@ -0,0 +1,671 @@
+#include <assert.h>
+#include <string.h>
+
+#include <oslib/font.h>
+#include <oslib/hourglass.h>
+#include <oslib/os.h>
+#include <oslib/osfscontrol.h>
+#include <oslib/taskwindow.h>
+#include <oslib/wimp.h>
+#include <oslib/wimpreadsysinfo.h>
+
+#include "harness-priv.h"
+
+static os_error font_no_font = { error_FONT_NO_FONT, "Undefined font handle" };
+static os_error font_bad_font_number = {
+ error_FONT_BAD_FONT_NUMBER,
+ "Font handle out of range"
+};
+static os_error font_not_found = { error_FONT_NOT_FOUND, "Font not found" };
+static os_error font_encoding_not_found = {
+ error_FONT_ENCODING_NOT_FOUND,
+ "Encoding not found"
+};
+static os_error font_no_handles = {
+ error_FONT_NO_HANDLES,
+ "No more font handles"
+};
+static os_error font_reserved = {
+ error_FONT_RESERVED,
+ "Reserved fields must be zero"
+};
+static os_error buff_overflow = { error_BUFF_OVERFLOW, "Buffer overflow" };
+static os_error bad_parameters = { error_BAD_PARAMETERS, "Bad parameters" };
+static os_error no_such_swi = { error_NO_SUCH_SWI, "SWI not known" };
+static os_error unimplemented = { error_UNIMPLEMENTED, "Not implemented" };
+
+/****************************************************************************/
+
+os_error *xfont_cache_addr (int *version, int *cache_size, int *cache_used)
+{
+ if (version != NULL)
+ *version = h->fm_version;
+ if (cache_size != NULL)
+ *cache_size = 512 * 1024;
+ if (cache_used != NULL)
+ *cache_used = 0;
+
+ return NULL;
+}
+
+os_error *xfont_find_font (char const *font_name, int xsize, int ysize,
+ int xres, int yres, font_f *font, int *xres_out, int *yres_out)
+{
+ char name[80], encoding[80];
+ const char *slash;
+ size_t ni, ei;
+ int fh;
+
+ /* Default xres and yres */
+ if (xres <= 0)
+ xres = 90;
+ if (yres <= 0)
+ yres = 90;
+
+ /* Parse font name */
+ slash = strchr(font_name, '\\');
+ if (slash == NULL) {
+ /* Bare name: symbol font */
+ strncpy(name, font_name, sizeof(name));
+ name[sizeof(name)-1] = '\0';
+ strcpy(encoding, "Symbol");
+ } else {
+ /* Identifier: extract encoding */
+ strncpy(name, font_name, slash - font_name);
+ name[slash-font_name] = '\0';
+ assert(slash[1] == 'E');
+ strncpy(encoding, slash + 2, sizeof(encoding));
+ encoding[sizeof(encoding)-1] = '\0';
+ }
+
+ /* Determine if we know about this font name */
+ for (ni = 0; ni < h->n_font_names; ni++) {
+ if (strcmp(h->font_names[ni], name) == 0)
+ break;
+ }
+ if (ni == h->n_font_names) {
+ return &font_not_found;
+ }
+
+ /* Determine if we know about this encoding */
+ if (strcmp("Symbol", encoding) == 0) {
+ ei = FONT_ENCODING_SYMBOL;
+ } else {
+ for (ei = 0; ei < h->n_encodings; ei++) {
+ if (strcmp(h->encodings[ei], encoding) == 0)
+ break;
+ }
+ if (ei == h->n_encodings) {
+ return &font_encoding_not_found;
+ }
+ }
+
+ /* Find existing font handle (0 is forbidden) */
+ for (fh = 1; fh < 256; fh++) {
+ if (h->fonts[fh].refcnt > 0 && h->fonts[fh].name == ni &&
+ h->fonts[fh].encoding == ei &&
+ h->fonts[fh].xsize == xsize &&
+ h->fonts[fh].ysize == ysize &&
+ h->fonts[fh].xres == xres &&
+ h->fonts[fh].yres == yres)
+ break;
+ }
+ if (fh == 256) {
+ /* No existing font found: allocate new one */
+ for (fh = 1; fh < 256; fh++) {
+ if (h->fonts[fh].refcnt == 0)
+ break;
+ }
+ if (fh == 256) {
+ return &font_no_handles;
+ }
+
+ h->fonts[fh].name = ni;
+ h->fonts[fh].encoding = ei;
+ h->fonts[fh].xsize = xsize;
+ h->fonts[fh].ysize = ysize;
+ h->fonts[fh].xres = xres;
+ h->fonts[fh].yres = yres;
+ }
+
+ /* Bump refcnt */
+ h->fonts[fh].refcnt++;
+
+ /* Set current font */
+ h->current_font = fh;
+
+ if (font != NULL)
+ *font = (font_f) fh;
+ if (xres_out != NULL)
+ *xres_out = xres;
+ if (yres_out != NULL)
+ *yres_out = yres;
+
+ return NULL;
+}
+
+os_error *xfont_lose_font (font_f font)
+{
+ if (font != 0 && h->fonts[font].refcnt > 0)
+ h->fonts[font].refcnt--;
+
+ return NULL;
+}
+
+os_error *xfont_read_info (font_f font, int *x0, int *y0, int *x1, int *y1)
+{
+ if (font == 0)
+ return &font_bad_font_number;
+ if (h->fonts[font].refcnt == 0)
+ return &font_no_font;
+
+ /* Cheat: just scale point size to OS units */
+ if (x0 != NULL)
+ *x0 = 0;
+ if (y0 != NULL)
+ *y0 = 0;
+ if (x1 != NULL)
+ *x1 = (h->fonts[font].xsize * 180) / (72 * 16);
+ if (y1 != NULL)
+ *y1 = (h->fonts[font].ysize * 180) / (72 * 16);
+
+ return NULL;
+}
+
+os_error *xfont_read_font_metrics (font_f font, font_bbox_info *bbox_info,
+ font_width_info *xwidth_info, font_width_info *ywidth_info,
+ font_metrics_misc_info *misc_info, font_kern_info *kern_info,
+ font_metric_flags *flags, int *bbox_info_size,
+ int *xwidth_info_size, int *ywidth_info_size,
+ int *misc_info_size, int *kern_info_size)
+{
+ if (font == 0)
+ return &font_bad_font_number;
+ if (h->fonts[font].refcnt == 0)
+ return &font_no_font;
+ if (bbox_info != NULL || xwidth_info != NULL || ywidth_info != NULL ||
+ kern_info != NULL || flags != NULL)
+ return &unimplemented;
+
+ if (misc_info != NULL) {
+ os_error *err = xfont_read_info(font,
+ &misc_info->x0, &misc_info->y0,
+ &misc_info->x1, &misc_info->y1);
+ if (err != NULL)
+ return err;
+ misc_info->xkern = misc_info->ykern = 0;
+ misc_info->italic_correction = 0;
+ misc_info->underline_position = 0;
+ misc_info->underline_thickness = 0;
+ misc_info->cap_height = misc_info->y1 - misc_info->y0;
+ misc_info->xheight = misc_info->cap_height >> 1;
+ misc_info->descender = misc_info->ascender = 0;
+ }
+
+ if (bbox_info_size != NULL)
+ *bbox_info_size = 0;
+ if (xwidth_info_size != NULL)
+ *xwidth_info_size = 0;
+ if (ywidth_info_size != NULL)
+ *ywidth_info_size = 0;
+ if (misc_info_size != NULL)
+ *misc_info_size = sizeof(font_metrics_misc_info);
+ if (kern_info_size != NULL)
+ *kern_info_size = 0;
+
+ return NULL;
+}
+
+os_error *xfont_read_encoding_filename (font_f font, char *buffer, int size,
+ char **end)
+{
+ const char *filename = NULL;
+ size_t ei;
+
+ if (font == 0)
+ return &font_bad_font_number;
+ if (h->fonts[font].refcnt == 0)
+ return &font_no_font;
+ if (h->encoding_filenames == NULL)
+ return &font_encoding_not_found;
+ if (h->fonts[font].encoding != FONT_ENCODING_SYMBOL) {
+ ei = h->fonts[font].encoding;
+ } else {
+ ei = h->n_encodings;
+ }
+ filename = h->encoding_filenames[
+ (h->fonts[font].name * (h->n_encodings + 1)) + ei];
+ if (filename == NULL)
+ return &font_encoding_not_found;
+ if (buffer == NULL || (size_t) size < strlen(filename) + 1)
+ return &bad_parameters;
+
+ strcpy(buffer, filename);
+
+ if (end != NULL)
+ *end = buffer + strlen(filename) + 1;
+
+ return NULL;
+}
+
+os_error *xfont_list_fonts (byte *buffer1, font_list_context context,
+ int size1, byte *buffer2, int size2, char const *tick_font,
+ font_list_context *context_out, int *used1, int *used2)
+{
+ const char **values;
+ size_t n_values;
+ size_t index = (context & 0xffff);
+
+ if ((context & font_RETURN_FONT_MENU) &&
+ (context & ~(font_USE_LINEFEED |
+ font_RETURN_FONT_MENU |
+ font_ALLOW_SYSTEM_FONT |
+ font_GIVEN_TICK | 0x400000)) >> 16)
+ return &bad_parameters;
+ if (!(context & font_RETURN_FONT_MENU) &&
+ (context & ~(font_RETURN_FONT_NAME |
+ font_RETURN_LOCAL_FONT_NAME |
+ font_USE_LINEFEED | 0x400000)) >> 16)
+ return &bad_parameters;
+ if (context & font_RETURN_FONT_MENU)
+ return &unimplemented;
+
+ if (context & 0x400000) {
+ values = h->encodings;
+ n_values = h->n_encodings;
+ } else {
+ values = h->font_names;
+ n_values = h->n_font_names;
+ }
+
+ if (index < n_values) {
+ int len = (int) strlen(values[index]) + 1;
+ if (context & font_RETURN_FONT_NAME) {
+ if (buffer1 != NULL && size1 < len)
+ return &buff_overflow;
+ if (buffer1 != NULL)
+ strcpy((char *) buffer1, values[index]);
+ if (used1 != NULL)
+ *used1 = len;
+ }
+ if (context & font_RETURN_LOCAL_FONT_NAME) {
+ if (buffer2 != NULL && size2 < len)
+ return &buff_overflow;
+ if (buffer2 != NULL)
+ strcpy((char *) buffer2, values[index]);
+ if (used2 != NULL)
+ *used2 = len;
+ }
+ index++;
+ } else {
+ index = -1;
+ }
+
+ if (context_out != NULL)
+ *context_out = (font_list_context) index;
+
+ (void) tick_font;
+
+ return NULL;
+}
+
+os_error *xfont_set_font (font_f font)
+{
+ if (font == 0)
+ return &font_bad_font_number;
+ if (h->fonts[font].refcnt == 0)
+ return &font_no_font;
+
+ h->current_font = font;
+
+ return NULL;
+}
+
+os_error *xfont_paint (font_f font, char const *string,
+ font_string_flags flags, int xpos, int ypos,
+ font_paint_block const *block, os_trfm const *trfm, int length)
+{
+ if (!(flags & font_GIVEN_FONT) || font == 0)
+ font = h->current_font;
+ if (font == 0 || h->fonts[font].refcnt == 0)
+ return &font_no_font;
+
+ if (flags & font_GIVEN_FONT)
+ h->current_font = font;
+
+ //XXX:
+ //XXX: also, pay attention to redirection to buffer
+ (void) string;
+ (void) xpos;
+ (void) ypos;
+ (void) block;
+ (void) trfm;
+ (void) length;
+
+ return NULL;
+}
+
+os_error *xfont_scan_string (font_f font, char const *s,
+ font_string_flags flags, int x, int y, font_scan_block *block,
+ os_trfm const *trfm, int length, char **split_point,
+ int *x_out, int *y_out, int *num_split_chars)
+{
+ size_t advance = 1;
+ int width = 0;
+
+ if (!(flags & font_GIVEN_FONT) || font == 0)
+ font = h->current_font;
+ if (font == 0 || h->fonts[font].refcnt == 0)
+ return &font_no_font;
+
+ if ((flags & font_GIVEN_BLOCK) && block == NULL)
+ return &bad_parameters;
+ if ((flags & font_RETURN_BBOX) && !(flags & font_GIVEN_BLOCK))
+ return &bad_parameters;
+ if ((flags & font_GIVEN_BLOCK) && (block->space.x != 0 ||
+ block->space.y != 0 ||
+ block->letter.x != 0 ||
+ block->letter.y != 0 ||
+ block->split_char != -1))
+ return &unimplemented;
+
+ if ((flags & font_GIVEN32_BIT) && (flags & font_GIVEN16_BIT))
+ return &bad_parameters;
+
+ if (!(flags & font_GIVEN_LENGTH))
+ length = 0x7ffffffc;
+
+ if (flags & font_GIVEN32_BIT)
+ advance = 4;
+ else if (flags & font_GIVEN16_BIT)
+ advance = 2;
+
+ /* Consume up to length bytes of input */
+ while (length > 0) {
+ uint32_t c = 0, i;
+ int cwidth;
+ for (i = 0; i < advance; i++) {
+ c |= s[i] << (advance - i - 1);
+ }
+ s += advance;
+ length -= advance;
+
+ /* Regardless of length, stop on terminator */
+ if (c == 0 || c == 10 || c == 13)
+ break;
+
+ /* Just scale font size to millipoints and add on the width */
+ cwidth = ((h->fonts[font].xsize * 1000) >> 4);
+ if ((flags & font_RETURN_CARET_POS) && x > 0 &&
+ (width + cwidth/2) > x) {
+ /* Split point is less than half way through
+ * this character: exclude it */
+ s -= advance;
+ length += advance;
+ break;
+ }
+ width += cwidth;
+ //XXX: how is negative x meant to work?
+ if (x > 0 && width > x)
+ break;
+ }
+
+ if (flags & font_RETURN_BBOX) {
+ block->bbox.x0 = 0;
+ block->bbox.y0 = 0;
+ block->bbox.x1 = width;
+ block->bbox.y1 = (h->fonts[font].ysize * 1000) >> 4;
+ }
+
+ if (x_out != NULL)
+ *x_out = width;
+ if (y_out != NULL)
+ *y_out = (h->fonts[font].ysize * 1000) >> 4;
+ if (split_point != NULL)
+ *split_point = (char *) s;
+
+ (void) y;
+ (void) trfm;
+ (void) num_split_chars;
+
+ return NULL;
+}
+
+os_error *xfont_switch_output_to_buffer (font_output_flags flags,
+ byte *buffer, char **end)
+{
+ if ((intptr_t) buffer <= 0 && flags != 0)
+ return &font_reserved;
+ if (flags & ~(font_NO_OUTPUT | font_ADD_HINTS | font_ERROR_IF_BITMAP))
+ return &font_reserved;
+
+ if (end)
+ *end = h->buffer;
+
+ if ((intptr_t) buffer != -1) {
+ h->buffer = (char *) buffer;
+ h->buffer_flags = flags;
+ }
+
+ return NULL;
+}
+
+os_error *xfont_enumerate_characters (font_f font, int character,
+ int *next_character, int *internal_character_code)
+{
+ static int extchars[] = { 0x20, 0x21, 0x30, 0x31, 0x32, 0xa0, 0x10ac0, 0x20021, 0x30000, -1 };
+ static int intchars[] = { 1, 2, 3, 4, -1, 5, 6, 7, 8 };
+ size_t index = 0;
+ int next = -1, internal = -1;
+
+ if (!h->fm_ucs)
+ return &no_such_swi;
+
+ if (font == 0)
+ font = h->current_font;
+ if (font == 0)
+ return &font_bad_font_number;
+ if (h->fonts[font].refcnt == 0)
+ return &font_no_font;
+
+ /* Broken FEC: skip first chunk unless code is valid.
+ * (only 0x20 and 0x21 are valid in the first chunk here,
+ * so we simply need to skip over these if the code is
+ * less than 0x20 -- any other codes in the first chunk
+ * will just fall out of the usual "next code" logic)
+ */
+ if (h->fm_broken_fec && character < extchars[0])
+ index = 2;
+
+ for (; index < (sizeof(intchars)/sizeof(intchars[0])); index++) {
+ if (extchars[index] == character) {
+ /* Found: return it and compute next */
+ next = extchars[index+1];
+ internal = intchars[index];
+ break;
+ } else if (extchars[index] > character) {
+ /* Not found and won't be: compute next */
+ next = extchars[index];
+ internal = -1;
+ break;
+ }
+ }
+
+ if (next_character != NULL)
+ *next_character = next;
+ if (internal_character_code != NULL)
+ *internal_character_code = internal;
+
+ return NULL;
+}
+
+/****************************************************************************/
+
+os_error *xhourglass_on (void)
+{
+ return &unimplemented;
+}
+
+os_error *xhourglass_off (void)
+{
+ return &unimplemented;
+}
+
+os_error *xhourglass_percentage (int percent)
+{
+ (void) percent;
+
+ return &unimplemented;
+}
+
+os_error *xhourglass_leds (bits eor_mask, bits and_mask, bits *old_leds)
+{
+ (void) eor_mask;
+ (void) and_mask;
+ (void) old_leds;
+
+ return &unimplemented;
+}
+
+os_error *xhourglass_colours (os_colour sand, os_colour glass,
+ os_colour *old_sand, os_colour *old_glass)
+{
+ (void) sand;
+ (void) glass;
+ (void) old_sand;
+ (void) old_glass;
+
+ return &unimplemented;
+}
+
+/****************************************************************************/
+
+os_error *xos_read_monotonic_time (os_t *t)
+{
+ (void) t;
+
+ return &unimplemented;
+}
+
+os_error *xos_read_mode_variable (os_mode mode, os_mode_var var, int *var_val,
+ bits *psr)
+{
+ (void) mode;
+ (void) var;
+ (void) var_val;
+ (void) psr;
+
+ return &unimplemented;
+}
+
+/****************************************************************************/
+
+os_error *xosfscontrol_canonicalise_path (char const *path_name, char *buffer,
+ char const *var, char const *path, int size, int *spare)
+{
+ const char *prefix = "Resources:$.Fonts.";
+ size_t len = strlen(path_name) + strlen(prefix) + 1;
+
+ if (strcmp(var, "Font$Path") != 0 || path != NULL)
+ return &unimplemented;
+
+ if (buffer == NULL && size != 0)
+ return &bad_parameters;
+
+ if (buffer != NULL && size < (int) len)
+ return &buff_overflow;
+
+ if (buffer != NULL) {
+ strcpy(buffer, prefix);
+ strcpy(buffer + strlen(prefix), path_name);
+ }
+
+ if (spare != NULL)
+ *spare = size - len;
+
+ return NULL;
+}
+
+/****************************************************************************/
+
+os_error *xtaskwindowtaskinfo_window_task (osbool *window_task)
+{
+ (void) window_task;
+
+ return &unimplemented;
+}
+
+/****************************************************************************/
+
+os_error *xwimp_create_window (wimp_window const *window, wimp_w *w)
+{
+ (void) window;
+ (void) w;
+
+ return &unimplemented;
+}
+
+os_error *xwimp_delete_window (wimp_w w)
+{
+ (void) w;
+
+ return &unimplemented;
+}
+
+os_error *xwimp_get_window_state (wimp_window_state *state)
+{
+ (void) state;
+
+ return &unimplemented;
+}
+
+os_error *xwimp_open_window (wimp_open *open)
+{
+ (void) open;
+
+ return &unimplemented;
+}
+
+os_error *xwimp_set_icon_state (wimp_w w, wimp_i i, wimp_icon_flags eor_bits,
+ wimp_icon_flags clear_bits)
+{
+ (void) w;
+ (void) i;
+ (void) eor_bits;
+ (void) clear_bits;
+
+ return &unimplemented;
+}
+
+os_error *xwimp_resize_icon (wimp_w w, wimp_i i, int x0, int y0, int x1, int y1)
+{
+ (void) w;
+ (void) i;
+ (void) x0;
+ (void) y0;
+ (void) x1;
+ (void) y1;
+
+ return &unimplemented;
+}
+
+os_error *xwimp_poll (wimp_poll_flags mask, wimp_block *block, int *pollword,
+ wimp_event_no *event)
+{
+ (void) mask;
+ (void) block;
+ (void) pollword;
+ (void) event;
+
+ return &unimplemented;
+}
+
+/****************************************************************************/
+
+os_error *xwimpreadsysinfo_task (wimp_t *task, wimp_version_no *version)
+{
+ (void) task;
+ (void) version;
+
+ return &unimplemented;
+}
diff --git a/test/nofonts.c b/test/nofonts.c
new file mode 100644
index 0000000..557d764
--- /dev/null
+++ b/test/nofonts.c
@@ -0,0 +1,20 @@
+#include <stdio.h>
+
+#include "rufl.h"
+
+#include "harness.h"
+#include "testutils.h"
+
+int main(int argc, const char **argv)
+{
+ UNUSED(argc);
+ UNUSED(argv);
+
+ rufl_test_harness_init(380, true, false);
+
+ assert(rufl_FONT_MANAGER_ERROR == rufl_init());
+
+ printf("PASS\n");
+
+ return 0;
+}
diff --git a/test/oldfminit.c b/test/oldfminit.c
new file mode 100644
index 0000000..c554499
--- /dev/null
+++ b/test/oldfminit.c
@@ -0,0 +1,353 @@
+#include <ftw.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "rufl.h"
+
+/* dirty! */
+#include "../src/rufl_internal.h"
+
+#include "harness.h"
+#include "testutils.h"
+
+struct expumap {
+ char *fontname;
+ size_t num_umaps;
+};
+
+struct cfg {
+ const char *datadir;
+
+ struct expumap *expumaps;
+ size_t n_expumaps;
+};
+
+static char template[] = "/tmp/oldfminitXXXXXX";
+static const char *ptmp = NULL;
+static struct cfg cfg;
+
+static int ftw_cb(const char *path, const struct stat *sb,
+ int typeflag, struct FTW *ftwbuf)
+{
+ (void) sb;
+ (void) typeflag;
+ (void) ftwbuf;
+
+ remove(path);
+
+ return 0;
+}
+
+static void cleanup(void)
+{
+ if (cfg.expumaps != NULL) {
+ size_t i;
+
+ for (i = 0; i < cfg.n_expumaps; i++) {
+ free(cfg.expumaps[i].fontname);
+ }
+ free(cfg.expumaps);
+ }
+
+ if (ptmp == NULL)
+ return;
+
+ nftw(ptmp, ftw_cb, FOPEN_MAX, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+}
+
+static void parse_cfg(const char *path, struct cfg *cfg,
+ void (*cb)(struct cfg *cfg, const char *line, size_t len))
+{
+ FILE *fp;
+ char wbuf[4096];
+ size_t nleft = 0;
+
+ fp = fopen(path, "r");
+ assert(fp != NULL);
+
+ while (!feof(fp)) {
+ char buf[2048];
+ size_t nread;
+ const char *p, *s;
+
+ nread = fread(buf, 1, sizeof(buf), fp);
+ if (nread != sizeof(buf)) {
+ assert(ferror(fp) == 0);
+ }
+
+ memcpy(wbuf + nleft, buf, nread);
+ nleft += nread;
+
+ for (p = s = wbuf; p < wbuf + nleft; p++) {
+ if (*p == '\n') {
+ cb(cfg, s, p - s);
+ s = p+1;
+ }
+ }
+ if (s != wbuf + nleft) {
+ memmove(wbuf, s, p - s);
+ nleft = p - s;
+ } else {
+ nleft = 0;
+ }
+ assert(nleft < sizeof(buf));
+ }
+ assert(nleft == 0);
+
+ fclose(fp);
+}
+
+static void parse_expumaps(struct cfg *cfg, char *data, size_t len)
+{
+ char *p, *s;
+ const char *font = NULL, *count = NULL;
+ struct expumap *e;
+ size_t num_umaps;
+
+ for (p = s = data; p < data+len; p++) {
+ if (*p == ' ' || *p == '\t') {
+ *p = '\0';
+ if (s != p) {
+ if (font == NULL)
+ font = s;
+ else if (count == NULL)
+ count = s;
+ }
+ s = p+1;
+ }
+ }
+ if (count == NULL)
+ count = s;
+
+ num_umaps = strtoul(count, &p, 10);
+ assert((size_t)(p-count) == strlen(count));
+
+ e = realloc(cfg->expumaps, (cfg->n_expumaps + 1) * sizeof(*e));
+ assert(e != NULL);
+
+ cfg->expumaps = e;
+ cfg->expumaps[cfg->n_expumaps].fontname = strdup(font);
+ assert(cfg->expumaps[cfg->n_expumaps].fontname != NULL);
+ cfg->expumaps[cfg->n_expumaps].num_umaps = num_umaps;
+ cfg->n_expumaps++;
+}
+
+static void parse_directive(struct cfg *cfg, char *linecpy, size_t len)
+{
+ char *p, *s;
+ const char *directive = NULL;
+
+ for (p = s = linecpy; p < linecpy+len; p++) {
+ if (*p == ' ' || *p == '\t') {
+ *p = '\0';
+ if (s != p && directive == NULL) {
+ directive = s;
+ s = p+1;
+ break;
+ }
+ s = p+1;
+ }
+ }
+ if (directive == NULL)
+ directive = s;
+
+ if (strcmp("\%expumaps", directive) == 0) {
+ parse_expumaps(cfg, s, len - (s - linecpy));
+ }
+}
+
+static void parse_encoding(struct cfg *cfg, char *linecpy, size_t len)
+{
+ char *p, *s;
+ const char *font = NULL, *encoding = NULL, *file = NULL;
+ char *path;
+
+ for (p = s = linecpy; p < linecpy+len; p++) {
+ if (*p == ' ' || *p == '\t') {
+ *p = '\0';
+ if (s != p) {
+ if (font == NULL)
+ font = s;
+ else if (encoding == NULL)
+ encoding = s;
+ else if (file == NULL)
+ file = s;
+ }
+ s = p+1;
+ }
+ }
+ if (file == NULL)
+ file = s;
+
+ assert(font != NULL);
+ assert(encoding != NULL);
+ assert(file != NULL);
+
+ path = malloc(strlen(cfg->datadir) + strlen(file) + 2);
+ assert(path != NULL);
+ strcpy(path, cfg->datadir);
+ path[strlen(cfg->datadir)] = '/'; //XXX: platform-agnostic dirsep?
+ strcpy(path+strlen(cfg->datadir)+1, file);
+
+ rufl_test_harness_set_font_encoding(font, encoding, path);
+
+ free(path);
+}
+
+static void line_cb(struct cfg *cfg, const char *line, size_t len)
+{
+ char *linecpy;
+
+ if (len == 0 || line[0] == '#')
+ return;
+
+ linecpy = malloc(len + 1);
+ assert(linecpy != NULL);
+ memcpy(linecpy, line, len);
+ linecpy[len] = '\0';
+
+ if (line[0] == '%')
+ parse_directive(cfg, linecpy, len);
+ else
+ parse_encoding(cfg, linecpy, len);
+
+ free(linecpy);
+}
+
+static void read_config(const char *path, struct cfg *cfg)
+{
+ char *pathcpy;
+
+ pathcpy = strdup(path);
+ assert(pathcpy != NULL);
+
+ cfg->datadir = dirname(pathcpy);
+
+ parse_cfg(path, cfg, line_cb);
+
+ free(pathcpy);
+ cfg->datadir = NULL;
+}
+
+int main(int argc, const char **argv)
+{
+ int width, x;
+ size_t offset;
+ int32_t xkern, ykern, italic, ascent, descent, xheight, cap_height;
+ int32_t x_bearing, y_bearing, mwidth, mheight, x_advance, y_advance;
+ int8_t uline_position;
+ uint8_t uline_thickness;
+ os_box bbox;
+
+ assert(2 == argc);
+
+ ptmp = mkdtemp(template);
+ assert(NULL != ptmp);
+ atexit(cleanup);
+ chdir(ptmp);
+
+ rufl_test_harness_init(339, false, true);
+
+ read_config(argv[1], &cfg);
+
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(3 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+
+ if (cfg.expumaps != NULL) {
+ size_t i, j;
+ for (i = 0; i != cfg.n_expumaps; i++) {
+ for (j = 0; j != rufl_font_list_entries; j++) {
+ if (strcmp(cfg.expumaps[i].fontname, rufl_font_list[j].identifier) == 0) {
+ assert(cfg.expumaps[i].num_umaps == rufl_font_list[j].num_umaps);
+ }
+ }
+ }
+ }
+
+ assert(rufl_OK == rufl_font_metrics("Corpus", rufl_WEIGHT_500,
+ &bbox, &xkern, &ykern, &italic,
+ &ascent, &descent, &xheight, &cap_height,
+ &uline_position, &uline_thickness));
+ assert(0 == bbox.x0);
+ assert(2 == bbox.x1);
+ assert(0 == bbox.y0);
+ assert(2 == bbox.y1);
+ assert(0 == xkern);
+ assert(0 == ykern);
+ assert(0 == italic);
+ assert(0 == ascent);
+ assert(0 == descent);
+ assert((bbox.y1 - bbox.y0) == cap_height);
+ assert((cap_height / 2) == xheight);
+ assert(0 == uline_position);
+ assert(0 == uline_thickness);
+
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, &width));
+ assert(50 == width);
+
+ /* Place caret after first character */
+ assert(rufl_OK == rufl_x_to_offset("Homerton", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 25, &offset, &x));
+ assert(1 == offset);
+ assert(25 == x);
+
+ /* Attempt to split after first character. As the split point is
+ * coincident with the start of the second character, however,
+ * the split point is placed after it. */
+ assert(rufl_OK == rufl_split("Trinity", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 25, &offset, &x));
+ assert(3 == offset);
+ assert(50 == x);
+
+ /* Compute width of replacement character */
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "\xef\xbf\xbd", 3, &width));
+ assert(17 == width);
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "\xf0\xa0\x80\xa5", 4, &width));
+ assert(26 == width);
+
+ /* Measure font bounding box */
+ assert(rufl_OK == rufl_font_bbox("Corpus", rufl_WEIGHT_500, 160,
+ &bbox));
+ assert(0 == bbox.x0);
+ assert(25 == bbox.x1);
+ assert(0 == bbox.y0);
+ assert(25 == bbox.y1);
+
+ /* Trivial render */
+ assert(rufl_OK == rufl_paint("Trinity", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 0, 0, 0));
+
+ rufl_dump_state(true);
+
+ /* Obtain metrics for a glyph */
+ assert(rufl_OK == rufl_glyph_metrics("Homerton", rufl_WEIGHT_500, 160,
+ "!", 1, &x_bearing, &y_bearing,
+ &mwidth, &mheight, &x_advance, &y_advance));
+ assert(0 == x_bearing);
+ assert(10000 == y_bearing);
+ assert(10000 == mwidth);
+ assert(10000 == mheight);
+ assert(10000 == x_advance);
+ assert(10000 == y_advance);
+
+ rufl_quit();
+
+ /* Reinit -- should load cache */
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(3 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+ /* Done for real this time */
+ rufl_quit();
+
+ printf("PASS\n");
+
+ return 0;
+}
diff --git a/test/olducsinit.c b/test/olducsinit.c
new file mode 100644
index 0000000..ed3d846
--- /dev/null
+++ b/test/olducsinit.c
@@ -0,0 +1,140 @@
+#include <ftw.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "rufl.h"
+
+#include "harness.h"
+#include "testutils.h"
+
+static char template[] = "/tmp/olducsinitXXXXXX";
+static const char *ptmp = NULL;
+
+static int ftw_cb(const char *path, const struct stat *sb,
+ int typeflag, struct FTW *ftwbuf)
+{
+ (void) sb;
+ (void) typeflag;
+ (void) ftwbuf;
+
+ remove(path);
+
+ return 0;
+}
+
+static void cleanup(void)
+{
+ if (ptmp == NULL)
+ return;
+
+ nftw(ptmp, ftw_cb, FOPEN_MAX, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+}
+
+int main(int argc, const char **argv)
+{
+ int width, x;
+ size_t offset;
+ int32_t xkern, ykern, italic, ascent, descent, xheight, cap_height;
+ int32_t x_bearing, y_bearing, mwidth, mheight, x_advance, y_advance;
+ int8_t uline_position;
+ uint8_t uline_thickness;
+ os_box bbox;
+
+ UNUSED(argc);
+ UNUSED(argv);
+
+ ptmp = mkdtemp(template);
+ assert(NULL != ptmp);
+ atexit(cleanup);
+ chdir(ptmp);
+
+ rufl_test_harness_init(361, true, true);
+
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(3 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+
+ assert(rufl_OK == rufl_font_metrics("Corpus", rufl_WEIGHT_500,
+ &bbox, &xkern, &ykern, &italic,
+ &ascent, &descent, &xheight, &cap_height,
+ &uline_position, &uline_thickness));
+ assert(0 == bbox.x0);
+ assert(2 == bbox.x1);
+ assert(0 == bbox.y0);
+ assert(2 == bbox.y1);
+ assert(0 == xkern);
+ assert(0 == ykern);
+ assert(0 == italic);
+ assert(0 == ascent);
+ assert(0 == descent);
+ assert((bbox.y1 - bbox.y0) == cap_height);
+ assert((cap_height / 2) == xheight);
+ assert(0 == uline_position);
+ assert(0 == uline_thickness);
+
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, &width));
+ assert(50 == width);
+
+ /* Place caret after first character */
+ assert(rufl_OK == rufl_x_to_offset("Homerton", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 25, &offset, &x));
+ assert(1 == offset);
+ assert(25 == x);
+
+ /* Attempt to split after first character. As the split point is
+ * coincident with the start of the second character, however,
+ * the split point is placed after it. */
+ assert(rufl_OK == rufl_split("Trinity", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 25, &offset, &x));
+ assert(3 == offset);
+ assert(50 == x);
+
+ /* Compute width of replacement character */
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "\xef\xbf\xbd", 3, &width));
+ assert(17 == width);
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "\xf0\xa0\x80\xa5", 4, &width));
+ assert(26 == width);
+
+ /* Measure font bounding box */
+ assert(rufl_OK == rufl_font_bbox("Corpus", rufl_WEIGHT_500, 160,
+ &bbox));
+ assert(0 == bbox.x0);
+ assert(25 == bbox.x1);
+ assert(0 == bbox.y0);
+ assert(25 == bbox.y1);
+
+ /* Trivial render */
+ assert(rufl_OK == rufl_paint("Trinity", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 0, 0, 0));
+
+ /* Obtain metrics for a glyph */
+ assert(rufl_OK == rufl_glyph_metrics("Homerton", rufl_WEIGHT_500, 160,
+ "!", 1, &x_bearing, &y_bearing,
+ &mwidth, &mheight, &x_advance, &y_advance));
+ assert(0 == x_bearing);
+ assert(10000 == y_bearing);
+ assert(10000 == mwidth);
+ assert(10000 == mheight);
+ assert(10000 == x_advance);
+ assert(10000 == y_advance);
+
+ rufl_dump_state(true);
+
+ rufl_quit();
+
+ /* Reinit -- should load cache */
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(3 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+ /* Done for real this time */
+ rufl_quit();
+
+ printf("PASS\n");
+
+ return 0;
+}
diff --git a/test/rufl_chars.c b/test/rufl_chars.c
index 14a0fb6..64831ad 100644
--- a/test/rufl_chars.c
+++ b/test/rufl_chars.c
@@ -18,13 +18,13 @@
unsigned int font = 0;
unsigned int weight = rufl_WEIGHT_400;
bool italic = false;
+unsigned int plane = 0;
static rufl_code redraw(int x, int y, int y0, int y1);
static void try(rufl_code code, const char *context);
static void die(const char *error);
-
int main(void)
{
unsigned int i;
@@ -73,7 +73,7 @@ int main(void)
try(rufl_init(), "rufl_init");
- menu = malloc(wimp_SIZEOF_MENU(10 + rufl_family_list_entries));
+ menu = malloc(wimp_SIZEOF_MENU(27 + rufl_family_list_entries));
if (!menu)
die("Out of memory");
strcpy(menu->title_data.text, "Fonts");
@@ -99,23 +99,37 @@ int main(void)
(wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
(wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT);
strcpy(menu->entries[9].data.text, "Italic");
- for (i = 0; i != rufl_family_list_entries; i++) {
- menu->entries[10 + i].menu_flags = 0;
+ for (i = 0; i != 17; i++) {
+ menu->entries[10 + i].menu_flags =
+ (i == 16 ? wimp_MENU_SEPARATE :0);
menu->entries[10 + i].sub_menu = wimp_NO_SUB_MENU;
menu->entries[10 + i].icon_flags = wimp_ICON_TEXT |
+ (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
+ (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT);
+ strcpy(menu->entries[10 + i].data.text, "Plane 1");
+ menu->entries[10 + i].data.text[6] = '0' + (i+1)/10;
+ if (menu->entries[10 + i].data.text[6] == '0')
+ menu->entries[10 + i].data.text[6] = ' ';
+ menu->entries[10 + i].data.text[7] = '0' + (i+1)%10;
+ }
+ for (i = 0; i != rufl_family_list_entries; i++) {
+ menu->entries[27 + i].menu_flags = 0;
+ menu->entries[27 + i].sub_menu = wimp_NO_SUB_MENU;
+ menu->entries[27 + i].icon_flags = wimp_ICON_TEXT |
wimp_ICON_INDIRECTED |
(wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
(wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT);
- menu->entries[10 + i].data.indirected_text.text =
+ menu->entries[27 + i].data.indirected_text.text =
(char *) rufl_family_list[i];
- menu->entries[10 + i].data.indirected_text.validation =
+ menu->entries[27 + i].data.indirected_text.validation =
(char *) -1;
- menu->entries[10 + i].data.indirected_text.size =
+ menu->entries[27 + i].data.indirected_text.size =
strlen(rufl_family_list[i]);
}
menu->entries[3].menu_flags |= wimp_MENU_TICKED;
menu->entries[10].menu_flags |= wimp_MENU_TICKED;
- menu->entries[i + 9].menu_flags |= wimp_MENU_LAST;
+ menu->entries[27].menu_flags |= wimp_MENU_TICKED;
+ menu->entries[i + 26].menu_flags |= wimp_MENU_LAST;
error = xwimp_create_window((wimp_window *) &window, &w);
if (error)
@@ -194,11 +208,17 @@ int main(void)
} else if (block.selection.items[0] == 9) {
italic = !italic;
menu->entries[9].menu_flags ^= wimp_MENU_TICKED;
+ } else if (block.selection.items[0] <= 26) {
+ menu->entries[10 + plane].menu_flags ^=
+ wimp_MENU_TICKED;
+ plane = block.selection.items[0] - 10;
+ menu->entries[10 + plane].menu_flags ^=
+ wimp_MENU_TICKED;
} else {
- menu->entries[10 + font].menu_flags ^=
+ menu->entries[27 + font].menu_flags ^=
wimp_MENU_TICKED;
- font = block.selection.items[0] - 10;
- menu->entries[10 + font].menu_flags ^=
+ font = block.selection.items[0] - 27;
+ menu->entries[27 + font].menu_flags ^=
wimp_MENU_TICKED;
}
error = xwimp_force_redraw(w,
@@ -249,15 +269,21 @@ rufl_code redraw(int x, int y, int y0, int y1)
rufl_style style = weight | (italic ? rufl_SLANTED : 0);
for (u = y0 / 40 * 32; (int) u != (y1 / 40 + 1) * 32; u++) {
- if (u <= 0x7f)
- s[0] = u, l = 1;
- else if (u <= 0x7ff)
- s[0] = 0xc0 | (u >> 6),
- s[1] = 0x80 | (u & 0x3f), l = 2;
- else if (u <= 0xffff)
- s[0] = 0xe0 | (u >> 12),
- s[1] = 0x80 | ((u >> 6) & 0x3f),
- s[2] = 0x80 | (u & 0x3f), l = 3;
+ unsigned int c = (plane << 16) | u;
+ if (c <= 0x7f)
+ s[0] = c, l = 1;
+ else if (c <= 0x7ff)
+ s[0] = 0xc0 | (c >> 6),
+ s[1] = 0x80 | (c & 0x3f), l = 2;
+ else if (c <= 0xffff)
+ s[0] = 0xe0 | (c >> 12),
+ s[1] = 0x80 | ((c >> 6) & 0x3f),
+ s[2] = 0x80 | (c & 0x3f), l = 3;
+ else if (c <= 0x10ffff)
+ s[0] = 0xf0 | (c >> 18),
+ s[1] = 0x80 | ((c >> 12) & 0x3f),
+ s[2] = 0x80 | ((c >> 6) & 0x3f),
+ s[3] = 0x80 | (c & 0x3f), l = 4;
else
break;
s[l] = 0;
@@ -276,7 +302,7 @@ rufl_code redraw(int x, int y, int y0, int y1)
void try(rufl_code code, const char *context)
{
- char s[200];
+ char s[400];
if (code == rufl_OK)
return;
else if (code == rufl_OUT_OF_MEMORY)
diff --git a/test/rufl_test.c b/test/rufl_test.c
index 51a29d6..bc644f5 100644
--- a/test/rufl_test.c
+++ b/test/rufl_test.c
@@ -18,23 +18,24 @@ static int cubic_to(os_coord *control1, os_coord *control2, os_coord *to,
void *user);
static void callback(void *context,
const char *font_name, unsigned int font_size,
- const char *s8, unsigned short *s16, unsigned int n,
+ const uint8_t *s8, const uint32_t *s32, unsigned int n,
int x, int y);
int main(void)
{
- char utf8_test[] = "Hello, world! ὕαλον "
- "Uherské Hradiště. 𐀀";
+ const char utf8_test[] = "Hello, world! ὕαλον "
+ "Uherské Hradiště. 𐀀"
+ "\xf0\xa0\x80\xa1";
int width;
size_t char_offset;
int x;
int actual_x;
struct rufl_decomp_funcs funcs = { move_to, line_to, cubic_to };
- int bbox[4];
+ os_box bbox;
try(rufl_init(), "rufl_init");
- rufl_dump_state();
+ rufl_dump_state(false);
try(rufl_paint("NewHall", rufl_WEIGHT_400, 240,
utf8_test, sizeof utf8_test - 1,
1200, 1000, 0), "rufl_paint");
@@ -62,9 +63,9 @@ int main(void)
try(rufl_paint_callback("NewHall", rufl_WEIGHT_400, 240,
utf8_test, sizeof utf8_test - 1,
1200, 1000, callback, 0), "rufl_paint_callback");
- try(rufl_font_bbox("NewHall", rufl_WEIGHT_400, 240, bbox),
+ try(rufl_font_bbox("NewHall", rufl_WEIGHT_400, 240, &bbox),
"rufl_font_bbox");
- printf("bbox: %i %i %i %i\n", bbox[0], bbox[1], bbox[2], bbox[3]);
+ printf("bbox: %i %i %i %i\n", bbox.x0, bbox.y0, bbox.x1, bbox.y1);
rufl_quit();
return 0;
@@ -131,7 +132,7 @@ int cubic_to(os_coord *control1, os_coord *control2, os_coord *to,
void callback(void *context,
const char *font_name, unsigned int font_size,
- const char *s8, unsigned short *s16, unsigned int n,
+ const uint8_t *s8, const uint32_t *s32, unsigned int n,
int x, int y)
{
(void) context;
@@ -140,9 +141,9 @@ void callback(void *context,
if (s8)
printf("s8 \"%.*s\" ", n, s8);
else {
- printf("s16 \"");
+ printf("s32 \"");
for (unsigned int i = 0; i != n; i++)
- printf("%x ", (unsigned int) s16[i]);
+ printf("%x ", (unsigned int) s32[i]);
printf("\" ");
}
printf("%i %i\n", x, y);
diff --git a/test/testutils.h b/test/testutils.h
new file mode 100644
index 0000000..7fe6333
--- /dev/null
+++ b/test/testutils.h
@@ -0,0 +1,123 @@
+#ifndef test_testutils_h_
+#define test_testutils_h_
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef UNUSED
+#define UNUSED(x) ((void) (x))
+#endif
+
+/* Redefine assert, so we can simply use the standard assert mechanism
+ * within testcases and exit with the right output for the testrunner
+ * to do the right thing. */
+void __assert2(const char *expr, const char *function,
+ const char *file, int line);
+
+void __assert2(const char *expr, const char *function,
+ const char *file, int line)
+{
+ UNUSED(function);
+ UNUSED(file);
+
+ printf("FAIL - %s at line %d\n", expr, line);
+
+ exit(EXIT_FAILURE);
+}
+
+#define assert(expr) \
+ ((void) ((expr) || (__assert2 (#expr, __func__, __FILE__, __LINE__), 0)))
+
+
+typedef bool (*line_func)(const char *data, size_t datalen, void *pw);
+
+static size_t parse_strlen(const char *str, size_t limit);
+bool parse_testfile(const char *filename, line_func callback, void *pw);
+size_t parse_filesize(const char *filename);
+
+/**
+ * Testcase datafile parser driver
+ *
+ * \param filename Name of file to parse
+ * \param callback Pointer to function to handle each line of input data
+ * \param pw Pointer to client-specific private data
+ * \return true on success, false otherwise.
+ */
+bool parse_testfile(const char *filename, line_func callback, void *pw)
+{
+ FILE *fp;
+ char buf[300];
+
+ fp = fopen(filename, "rb");
+ if (fp == NULL) {
+ printf("Failed opening %s\n", filename);
+ return false;
+ }
+
+ while (fgets(buf, sizeof buf, fp)) {
+ if (buf[0] == '\n')
+ continue;
+
+ if (!callback(buf, parse_strlen(buf, sizeof buf - 1), pw)) {
+ fclose(fp);
+ return false;
+ }
+ }
+
+ fclose(fp);
+
+ return true;
+}
+
+/**
+ * Utility string length measurer; assumes strings are '\n' terminated
+ *
+ * \param str String to measure length of
+ * \param limit Upper bound on string length
+ * \return String length
+ */
+size_t parse_strlen(const char *str, size_t limit)
+{
+ size_t len = 0;
+
+ if (str == NULL)
+ return 0;
+
+ while (len < limit - 1 && *str != '\n') {
+ len++;
+ str++;
+ }
+
+ len++;
+
+ return len;
+}
+
+/**
+ * Read the size of a file
+ *
+ * \param filename Name of file to read size of
+ * \return File size (in bytes), or 0 on error
+ */
+size_t parse_filesize(const char *filename)
+{
+ FILE *fp;
+ size_t len = 0;
+
+ fp = fopen(filename, "rb");
+ if (fp == NULL) {
+ printf("Failed opening %s\n", filename);
+ return 0;
+ }
+
+ fseek(fp, 0, SEEK_END);
+ len = ftell(fp);
+
+ fclose(fp);
+
+ return len;
+}
+
+
+#endif
diff --git a/test/ucsinit.c b/test/ucsinit.c
new file mode 100644
index 0000000..25aea60
--- /dev/null
+++ b/test/ucsinit.c
@@ -0,0 +1,140 @@
+#include <ftw.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "rufl.h"
+
+#include "harness.h"
+#include "testutils.h"
+
+static char template[] = "/tmp/ucsinitXXXXXX";
+static const char *ptmp = NULL;
+
+static int ftw_cb(const char *path, const struct stat *sb,
+ int typeflag, struct FTW *ftwbuf)
+{
+ (void) sb;
+ (void) typeflag;
+ (void) ftwbuf;
+
+ remove(path);
+
+ return 0;
+}
+
+static void cleanup(void)
+{
+ if (ptmp == NULL)
+ return;
+
+ nftw(ptmp, ftw_cb, FOPEN_MAX, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+}
+
+int main(int argc, const char **argv)
+{
+ int width, x;
+ size_t offset;
+ int32_t xkern, ykern, italic, ascent, descent, xheight, cap_height;
+ int32_t x_bearing, y_bearing, mwidth, mheight, x_advance, y_advance;
+ int8_t uline_position;
+ uint8_t uline_thickness;
+ os_box bbox;
+
+ UNUSED(argc);
+ UNUSED(argv);
+
+ ptmp = mkdtemp(template);
+ assert(NULL != ptmp);
+ atexit(cleanup);
+ chdir(ptmp);
+
+ rufl_test_harness_init(380, true, true);
+
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(3 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+
+ assert(rufl_OK == rufl_font_metrics("Corpus", rufl_WEIGHT_500,
+ &bbox, &xkern, &ykern, &italic,
+ &ascent, &descent, &xheight, &cap_height,
+ &uline_position, &uline_thickness));
+ assert(0 == bbox.x0);
+ assert(2 == bbox.x1);
+ assert(0 == bbox.y0);
+ assert(2 == bbox.y1);
+ assert(0 == xkern);
+ assert(0 == ykern);
+ assert(0 == italic);
+ assert(0 == ascent);
+ assert(0 == descent);
+ assert((bbox.y1 - bbox.y0) == cap_height);
+ assert((cap_height / 2) == xheight);
+ assert(0 == uline_position);
+ assert(0 == uline_thickness);
+
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, &width));
+ assert(50 == width);
+
+ /* Place caret after first character */
+ assert(rufl_OK == rufl_x_to_offset("Homerton", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 25, &offset, &x));
+ assert(1 == offset);
+ assert(25 == x);
+
+ /* Attempt to split after first character. As the split point is
+ * coincident with the start of the second character, however,
+ * the split point is placed after it. */
+ assert(rufl_OK == rufl_split("Trinity", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 25, &offset, &x));
+ assert(3 == offset);
+ assert(50 == x);
+
+ /* Compute width of replacement character */
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "\xef\xbf\xbd", 3, &width));
+ assert(17 == width);
+ assert(rufl_OK == rufl_width("Corpus", rufl_WEIGHT_500, 160,
+ "\xf0\xa0\x80\xa5", 4, &width));
+ assert(26 == width);
+
+ /* Measure font bounding box */
+ assert(rufl_OK == rufl_font_bbox("Corpus", rufl_WEIGHT_500, 160,
+ &bbox));
+ assert(0 == bbox.x0);
+ assert(25 == bbox.x1);
+ assert(0 == bbox.y0);
+ assert(25 == bbox.y1);
+
+ /* Trivial render */
+ assert(rufl_OK == rufl_paint("Trinity", rufl_WEIGHT_500, 160,
+ "!\xc2\xa0", 3, 0, 0, 0));
+
+ /* Obtain metrics for a glyph */
+ assert(rufl_OK == rufl_glyph_metrics("Homerton", rufl_WEIGHT_500, 160,
+ "!", 1, &x_bearing, &y_bearing,
+ &mwidth, &mheight, &x_advance, &y_advance));
+ assert(0 == x_bearing);
+ assert(10000 == y_bearing);
+ assert(10000 == mwidth);
+ assert(10000 == mheight);
+ assert(10000 == x_advance);
+ assert(10000 == y_advance);
+
+ rufl_dump_state(true);
+
+ rufl_quit();
+
+ /* Reinit -- should load cache */
+ assert(rufl_OK == rufl_init());
+ assert(NULL == rufl_fm_error);
+ assert(3 == rufl_family_list_entries);
+ assert(NULL != rufl_family_menu);
+ /* Done for real this time */
+ rufl_quit();
+
+ printf("PASS\n");
+
+ return 0;
+}