test-conf.c revision a32e9e42
1/*
2 * fontconfig/test/test-conf.c
3 *
4 * Copyright © 2000 Keith Packard
5 * Copyright © 2018 Akira TAGOH
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of the author(s) not be used in
12 * advertising or publicity pertaining to distribution of the software without
13 * specific, written prior permission.  The authors make no
14 * representations about the suitability of this software for any purpose.  It
15 * is provided "as is" without express or implied warranty.
16 *
17 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
19 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
20 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23 * PERFORMANCE OF THIS SOFTWARE.
24 */
25#include <stdio.h>
26#include <string.h>
27#include <fontconfig/fontconfig.h>
28#include <json.h>
29
30struct _FcConfig {
31    FcStrSet	*configDirs;	    /* directories to scan for fonts */
32    FcStrSet	*fontDirs;
33    FcStrSet	*cacheDirs;
34    FcStrSet	*configFiles;	    /* config files loaded */
35    void	*subst[FcMatchKindEnd];
36    int		maxObjects;	    /* maximum number of tests in all substs */
37    FcStrSet	*acceptGlobs;
38    FcStrSet	*rejectGlobs;
39    FcFontSet	*acceptPatterns;
40    FcFontSet	*rejectPatterns;
41    FcFontSet	*fonts[FcSetApplication + 1];
42};
43
44static FcPattern *
45build_pattern (json_object *obj)
46{
47    json_object_iter iter;
48    FcPattern *pat = FcPatternCreate ();
49
50    json_object_object_foreachC (obj, iter)
51    {
52	FcValue v;
53
54	if (json_object_get_type (iter.val) == json_type_boolean)
55	{
56	    v.type = FcTypeBool;
57	    v.u.b = json_object_get_boolean (iter.val);
58	}
59	else if (json_object_get_type (iter.val) == json_type_double)
60	{
61	    v.type = FcTypeDouble;
62	    v.u.d = json_object_get_double (iter.val);
63	}
64	else if (json_object_get_type (iter.val) == json_type_int)
65	{
66	    v.type = FcTypeInteger;
67	    v.u.i = json_object_get_int (iter.val);
68	}
69	else if (json_object_get_type (iter.val) == json_type_string)
70	{
71	    v.type = FcTypeString;
72	    v.u.s = json_object_get_string (iter.val);
73	}
74	else if (json_object_get_type (iter.val) == json_type_null)
75	{
76	    v.type = FcTypeVoid;
77	}
78	else
79	{
80	    fprintf (stderr, "W: unexpected object to build a pattern: (%s %s)", iter.key, json_type_to_name (json_object_get_type (iter.val)));
81	    continue;
82	}
83	FcPatternAdd (pat, iter.key, v, FcTrue);
84    }
85    return pat;
86}
87
88static FcBool
89build_fonts (FcConfig *config, json_object *root)
90{
91    json_object *fonts;
92    FcFontSet *fs;
93    int i, n;
94
95    if (!json_object_object_get_ex (root, "fonts", &fonts) ||
96	json_object_get_type (fonts) != json_type_array)
97    {
98	fprintf (stderr, "W: No fonts defined\n");
99	return FcFalse;
100    }
101    fs = FcFontSetCreate ();
102    n = json_object_array_length (fonts);
103    for (i = 0; i < n; i++)
104    {
105	json_object *obj = json_object_array_get_idx (fonts, i);
106	FcPattern *pat;
107
108	if (json_object_get_type (obj) != json_type_object)
109	    continue;
110	pat = build_pattern (obj);
111	FcFontSetAdd (fs, pat);
112    }
113    /* FcConfigSetFonts (config, fs, FcSetSystem); */
114    if (config->fonts[FcSetSystem])
115	FcFontSetDestroy (config->fonts[FcSetSystem]);
116    config->fonts[FcSetSystem] = fs;
117
118    return FcTrue;
119}
120
121static FcBool
122run_test (FcConfig *config, json_object *root)
123{
124    json_object *tests;
125    FcFontSet *fs;
126    int i, n, fail = 0;
127
128    if (!json_object_object_get_ex (root, "tests", &tests) ||
129	json_object_get_type (tests) != json_type_array)
130    {
131	fprintf (stderr, "W: No test cases defined\n");
132	return FcFalse;
133    }
134    fs = FcFontSetCreate ();
135    n = json_object_array_length (tests);
136    for (i = 0; i < n; i++)
137    {
138	json_object *obj = json_object_array_get_idx (tests, i);
139	json_object_iter iter;
140	FcPattern *query, *result;
141	const char *method;
142
143	if (json_object_get_type (obj) != json_type_object)
144	    continue;
145	json_object_object_foreachC (obj, iter)
146	{
147	    if (strcmp (iter.key, "method") == 0)
148	    {
149		if (json_object_get_type (iter.val) != json_type_string)
150		{
151		    fprintf (stderr, "W: invalid type of method: (%s)\n", json_type_to_name (json_object_get_type (iter.val)));
152		    continue;
153		}
154		method = json_object_get_string (iter.val);
155	    }
156	    else if (strcmp (iter.key, "query") == 0)
157	    {
158		if (json_object_get_type (iter.val) != json_type_object)
159		{
160		    fprintf (stderr, "W: invalid type of query: (%s)\n", json_type_to_name (json_object_get_type (iter.val)));
161		    continue;
162		}
163		query = build_pattern (iter.val);
164	    }
165	    else if (strcmp (iter.key, "result") == 0)
166	    {
167		if (json_object_get_type (iter.val) != json_type_object)
168		{
169		    fprintf (stderr, "W: invalid type of result: (%s)\n", json_type_to_name (json_object_get_type (iter.val)));
170		    continue;
171		}
172		result = build_pattern (iter.val);
173	    }
174	    else
175	    {
176		fprintf (stderr, "W: unknown object: %s\n", iter.key);
177	    }
178	}
179	if (strcmp (method, "match") == 0)
180	{
181	    FcPattern *match;
182	    FcResult res;
183
184	    FcConfigSubstitute (config, query, FcMatchPattern);
185	    FcDefaultSubstitute (query);
186	    match = FcFontMatch (config, query, &res);
187	    if (match)
188	    {
189		FcPatternIter iter;
190		int x, vc;
191
192		FcPatternIterStart (result, &iter);
193		do
194		{
195		    vc = FcPatternIterValueCount (result, &iter);
196		    for (x = 0; x < vc; x++)
197		    {
198			FcValue vr, vm;
199
200			if (FcPatternIterGetValue (result, &iter, x, &vr, NULL) != FcResultMatch)
201			{
202			    fprintf (stderr, "E: unable to obtain a value from the expected result\n");
203			    fail++;
204			    goto bail;
205			}
206			if (FcPatternGet (match, FcPatternIterGetObject (result, &iter), x, &vm) != FcResultMatch)
207			{
208			    vm.type = FcTypeVoid;
209			}
210			if (!FcValueEqual (vm, vr))
211			{
212			    printf ("E: failed to compare %s:\n", FcPatternIterGetObject (result, &iter));
213			    printf ("   actual result:");
214			    FcValuePrint (vm);
215			    printf ("\n   expected result:");
216			    FcValuePrint (vr);
217			    printf ("\n");
218			    fail++;
219			    goto bail;
220			}
221		    }
222		} while (FcPatternIterNext (result, &iter));
223	    bail:;
224	    }
225	    else
226	    {
227		fprintf (stderr, "E: no match\n");
228		fail++;
229	    }
230	}
231	else
232	{
233	    fprintf (stderr, "W: unknown testing method: %s\n", method);
234	}
235    }
236
237    return fail == 0;
238}
239
240static FcBool
241run_scenario (FcConfig *config, char *file)
242{
243    FcBool ret = FcTrue;
244    json_object *root, *scenario;
245
246    root = json_object_from_file (file);
247    if (!root)
248    {
249	fprintf (stderr, "E: Unable to read the file: %s\n", file);
250	return FcFalse;
251    }
252    if (!build_fonts (config, root))
253    {
254	ret = FcFalse;
255	goto bail1;
256    }
257    if (!run_test (config, root))
258    {
259	ret = FcFalse;
260	goto bail1;
261    }
262
263bail1:
264    json_object_put (root);
265
266    return ret;
267}
268
269static FcBool
270load_config (FcConfig *config, char *file)
271{
272    FILE *fp;
273    long len;
274    char *buf = NULL;
275    FcBool ret = FcTrue;
276
277    if ((fp = fopen(file, "rb")) == NULL)
278	return FcFalse;
279    fseek (fp, 0L, SEEK_END);
280    len = ftell (fp);
281    fseek (fp, 0L, SEEK_SET);
282    buf = malloc (sizeof (char) * (len + 1));
283    if (!buf)
284    {
285	ret = FcFalse;
286	goto bail1;
287    }
288    fread (buf, (size_t)len, sizeof (char), fp);
289    buf[len] = 0;
290
291    ret = FcConfigParseAndLoadFromMemory (config, buf, FcTrue);
292bail1:
293    fclose (fp);
294    if (buf)
295	free (buf);
296
297    return ret;
298}
299
300int
301main (int argc, char **argv)
302{
303    FcConfig *config;
304    int retval = 0;
305
306    if (argc < 3)
307    {
308	fprintf(stderr, "Usage: %s <conf file> <test scenario>\n", argv[0]);
309	return 1;
310    }
311
312    config = FcConfigCreate ();
313    if (!load_config (config, argv[1]))
314    {
315	fprintf(stderr, "E: Failed to load config\n");
316	retval = 1;
317	goto bail1;
318    }
319    if (!run_scenario (config, argv[2]))
320    {
321	retval = 1;
322	goto bail1;
323    }
324bail1:
325    FcConfigDestroy (config);
326
327    return retval;
328}
329