Merge branch 'upstream' into tizen
[platform/upstream/fontconfig.git] / test / test-conf.c
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
30 struct _FcConfig {
31     FcStrSet    *configDirs;        /* directories to scan for fonts */
32     FcStrSet    *configMapDirs;
33     FcStrSet    *fontDirs;
34     FcStrSet    *cacheDirs;
35     FcStrSet    *configFiles;       /* config files loaded */
36     void        *subst[FcMatchKindEnd];
37     int         maxObjects;         /* maximum number of tests in all substs */
38     FcStrSet    *acceptGlobs;
39     FcStrSet    *rejectGlobs;
40     FcFontSet   *acceptPatterns;
41     FcFontSet   *rejectPatterns;
42     FcFontSet   *fonts[FcSetApplication + 1];
43 };
44
45 static FcPattern *
46 build_pattern (json_object *obj)
47 {
48     json_object_iter iter;
49     FcPattern *pat = FcPatternCreate ();
50
51     json_object_object_foreachC (obj, iter)
52     {
53         FcValue v;
54         FcBool destroy_v = FcFalse;
55         FcMatrix matrix;
56
57         if (json_object_get_type (iter.val) == json_type_boolean)
58         {
59             v.type = FcTypeBool;
60             v.u.b = json_object_get_boolean (iter.val);
61         }
62         else if (json_object_get_type (iter.val) == json_type_double)
63         {
64             v.type = FcTypeDouble;
65             v.u.d = json_object_get_double (iter.val);
66         }
67         else if (json_object_get_type (iter.val) == json_type_int)
68         {
69             v.type = FcTypeInteger;
70             v.u.i = json_object_get_int (iter.val);
71         }
72         else if (json_object_get_type (iter.val) == json_type_string)
73         {
74             const FcObjectType *o = FcNameGetObjectType (iter.key);
75             if (o && (o->type == FcTypeRange || o->type == FcTypeDouble || o->type == FcTypeInteger))
76             {
77                 const FcConstant *c = FcNameGetConstant ((const FcChar8 *) json_object_get_string (iter.val));
78                 if (!c) {
79                     fprintf (stderr, "E: value is not a known constant\n");
80                     fprintf (stderr, "   key: %s\n", iter.key);
81                     fprintf (stderr, "   val: %s\n", json_object_get_string (iter.val));
82                     continue;
83                 }
84                 if (strcmp (c->object, iter.key) != 0)
85                 {
86                     fprintf (stderr, "E: value is a constant of different object\n");
87                     fprintf (stderr, "   key: %s\n", iter.key);
88                     fprintf (stderr, "   val: %s\n", json_object_get_string (iter.val));
89                     fprintf (stderr, "   key implied by value: %s\n", c->object);
90                     continue;
91                 }
92                 v.type = FcTypeInteger;
93                 v.u.i = c->value;
94             }
95             else if (strcmp (json_object_get_string (iter.val), "DontCare") == 0)
96             {
97                 v.type = FcTypeBool;
98                 v.u.b = FcDontCare;
99             }
100             else
101             {
102                 v.type = FcTypeString;
103                 v.u.s = (const FcChar8 *) json_object_get_string (iter.val);
104             }
105         }
106         else if (json_object_get_type (iter.val) == json_type_null)
107         {
108             v.type = FcTypeVoid;
109         }
110         else if (json_object_get_type (iter.val) == json_type_array)
111         {
112             json_object *o;
113             json_type type;
114             int i, n;
115
116             n = json_object_array_length (iter.val);
117             if (n == 0) {
118                 fprintf (stderr, "E: value is an empty array\n");
119                 continue;
120             }
121
122             o = json_object_array_get_idx (iter.val, 0);
123             type = json_object_get_type (o);
124             if (type == json_type_string) {
125                 const FcObjectType *fc_o = FcNameGetObjectType (iter.key);
126                 if (fc_o && fc_o->type == FcTypeCharSet) {
127                     FcCharSet* cs = FcCharSetCreate ();
128                     if (!cs) {
129                         fprintf (stderr, "E: failed to create charset\n");
130                         continue;
131                     }
132                     v.type = FcTypeCharSet;
133                     v.u.c = cs;
134                     destroy_v = FcTrue;
135                     for (i = 0; i < n; i++)
136                     {
137                         const FcChar8 *src;
138                         int len, nchar, wchar;
139                         FcBool valid;
140                         FcChar32 dst;
141
142                         o = json_object_array_get_idx (iter.val, i);
143                         type = json_object_get_type (o);
144                         if (type != json_type_string) {
145                             fprintf (stderr, "E: charset value not string\n");
146                             FcValueDestroy (v);
147                             continue;
148                         }
149                         src = (const FcChar8 *) json_object_get_string (o);
150                         len = json_object_get_string_len (o);
151                         valid = FcUtf8Len (src, len, &nchar, &wchar);
152                         if (valid == FcFalse) {
153                             fprintf (stderr, "E: charset entry not well formed\n");
154                             FcValueDestroy (v);
155                             continue;
156                         }
157                         if (nchar != 1) {
158                             fprintf (stderr, "E: charset entry not not one codepoint\n");
159                             FcValueDestroy (v);
160                             continue;
161                         }
162                         FcUtf8ToUcs4 (src, &dst, len);
163                         if (FcCharSetAddChar (cs, dst) == FcFalse) {
164                             fprintf (stderr, "E: failed to add to charset\n");
165                             FcValueDestroy (v);
166                             continue;
167                         }
168                     }
169                 } else if (fc_o && fc_o->type == FcTypeString) {
170                     for (i = 0; i < n; i++)
171                     {
172                         o = json_object_array_get_idx (iter.val, i);
173                         type = json_object_get_type (o);
174                         if (type != json_type_string) {
175                             fprintf (stderr, "E: unable to convert to string\n");
176                             continue;
177                         }
178                         v.type = FcTypeString;
179                         v.u.s = (const FcChar8 *) json_object_get_string (o);
180                         FcPatternAdd (pat, iter.key, v, FcTrue);
181                         v.type = FcTypeVoid;
182                     }
183                     continue;
184                 } else {
185                     FcLangSet* ls = FcLangSetCreate ();
186                     if (!ls) {
187                         fprintf (stderr, "E: failed to create langset\n");
188                         continue;
189                     }
190                     v.type = FcTypeLangSet;
191                     v.u.l = ls;
192                     destroy_v = FcTrue;
193                     for (i = 0; i < n; i++)
194                     {
195                         o = json_object_array_get_idx (iter.val, i);
196                         type = json_object_get_type (o);
197                         if (type != json_type_string) {
198                             fprintf (stderr, "E: langset value not string\n");
199                             FcValueDestroy (v);
200                             continue;
201                         }
202                         if (FcLangSetAdd (ls, (const FcChar8 *)json_object_get_string (o)) == FcFalse) {
203                             fprintf (stderr, "E: failed to add to langset\n");
204                             FcValueDestroy (v);
205                             continue;
206                         }
207                     }
208                 }
209             } else if (type == json_type_double || type == json_type_int) {
210                 const FcObjectType *fc_o = FcNameGetObjectType (iter.key);
211                 double values[4];
212
213                 if (fc_o && fc_o->type == FcTypeDouble) {
214                     for (i = 0; i < n; i++)
215                     {
216                         o = json_object_array_get_idx (iter.val, i);
217                         type = json_object_get_type (o);
218                         if (type == json_type_double) {
219                             v.type = FcTypeDouble;
220                             v.u.d = json_object_get_double (o);
221                         } else if (type == json_type_int) {
222                             v.type = FcTypeInteger;
223                             v.u.i = json_object_get_int (o);
224                         } else {
225                             fprintf (stderr, "E: unable to convert to double\n");
226                             continue;
227                         }
228                         FcPatternAdd (pat, iter.key, v, FcTrue);
229                         v.type = FcTypeVoid;
230                     }
231                     continue;
232                 } else {
233                     if (n != 2 && n != 4) {
234                         fprintf (stderr, "E: array starting with number not range or matrix\n");
235                         continue;
236                     }
237                     for (i = 0; i < n; i++) {
238                         o = json_object_array_get_idx (iter.val, i);
239                         type = json_object_get_type (o);
240                         if (type != json_type_double && type != json_type_int) {
241                             fprintf (stderr, "E: numeric array entry not a number\n");
242                             continue;
243                         }
244                         values[i] = json_object_get_double (o);
245                     }
246                     if (n == 2) {
247                         v.type = FcTypeRange;
248                         v.u.r = FcRangeCreateDouble (values[0], values[1]);
249                         if (!v.u.r) {
250                             fprintf (stderr, "E: failed to create range\n");
251                             continue;
252                         }
253                         destroy_v = FcTrue;
254                     } else {
255                         v.type = FcTypeMatrix;
256                         v.u.m = &matrix;
257                         matrix.xx = values[0];
258                         matrix.xy = values[1];
259                         matrix.yx = values[2];
260                         matrix.yy = values[3];
261                     }
262                 }
263             } else {
264                 fprintf (stderr, "E: array format not recognized\n");
265                 continue;
266             }
267         }
268         else
269         {
270             fprintf (stderr, "W: unexpected object to build a pattern: (%s %s)", iter.key, json_type_to_name (json_object_get_type (iter.val)));
271             continue;
272         }
273         if (v.type != FcTypeVoid)
274             FcPatternAdd (pat, iter.key, v, FcTrue);
275         if (destroy_v)
276             FcValueDestroy (v);
277     }
278     return pat;
279 }
280
281 static FcFontSet *
282 build_fs (json_object *obj)
283 {
284     FcFontSet *fs = FcFontSetCreate ();
285     int i, n;
286
287     n = json_object_array_length (obj);
288     for (i = 0; i < n; i++)
289     {
290         json_object *o = json_object_array_get_idx (obj, i);
291         FcPattern *pat;
292
293         if (json_object_get_type (o) != json_type_object)
294             continue;
295         pat = build_pattern (o);
296         FcFontSetAdd (fs, pat);
297     }
298
299     return fs;
300 }
301
302 static FcBool
303 build_fonts (FcConfig *config, json_object *root)
304 {
305     json_object *fonts;
306     FcFontSet *fs;
307
308     if (!json_object_object_get_ex (root, "fonts", &fonts) ||
309         json_object_get_type (fonts) != json_type_array)
310     {
311         fprintf (stderr, "W: No fonts defined\n");
312         return FcFalse;
313     }
314     fs = build_fs (fonts);
315     /* FcConfigSetFonts (config, fs, FcSetSystem); */
316     if (config->fonts[FcSetSystem])
317         FcFontSetDestroy (config->fonts[FcSetSystem]);
318     config->fonts[FcSetSystem] = fs;
319
320     return FcTrue;
321 }
322
323 static FcBool
324 run_test (FcConfig *config, json_object *root)
325 {
326     json_object *tests;
327     int i, n, fail = 0;
328
329     if (!json_object_object_get_ex (root, "tests", &tests) ||
330         json_object_get_type (tests) != json_type_array)
331     {
332         fprintf (stderr, "W: No test cases defined\n");
333         return FcFalse;
334     }
335     n = json_object_array_length (tests);
336     for (i = 0; i < n; i++)
337     {
338         json_object *obj = json_object_array_get_idx (tests, i);
339         json_object_iter iter;
340         FcPattern *query = NULL;
341         FcPattern *result = NULL;
342         FcFontSet *result_fs = NULL;
343         const char *method = NULL;
344
345         if (json_object_get_type (obj) != json_type_object)
346             continue;
347         json_object_object_foreachC (obj, iter)
348         {
349             if (strcmp (iter.key, "method") == 0)
350             {
351                 if (json_object_get_type (iter.val) != json_type_string)
352                 {
353                     fprintf (stderr, "W: invalid type of method: (%s)\n", json_type_to_name (json_object_get_type (iter.val)));
354                     continue;
355                 }
356                 method = json_object_get_string (iter.val);
357             }
358             else if (strcmp (iter.key, "query") == 0)
359             {
360                 if (json_object_get_type (iter.val) != json_type_object)
361                 {
362                     fprintf (stderr, "W: invalid type of query: (%s)\n", json_type_to_name (json_object_get_type (iter.val)));
363                     continue;
364                 }
365                 if (query)
366                     FcPatternDestroy (query);
367                 query = build_pattern (iter.val);
368             }
369             else if (strcmp (iter.key, "result") == 0)
370             {
371                 if (json_object_get_type (iter.val) != json_type_object)
372                 {
373                     fprintf (stderr, "W: invalid type of result: (%s)\n", json_type_to_name (json_object_get_type (iter.val)));
374                     continue;
375                 }
376                 if (result)
377                     FcPatternDestroy (result);
378                 result = build_pattern (iter.val);
379             }
380             else if (strcmp (iter.key, "result_fs") == 0)
381             {
382                 if (json_object_get_type (iter.val) != json_type_array)
383                 {
384                     fprintf (stderr, "W: invalid type of result_fs: (%s)\n", json_type_to_name (json_object_get_type (iter.val)));
385                     continue;
386                 }
387                 if (result_fs)
388                     FcFontSetDestroy (result_fs);
389                 result_fs = build_fs (iter.val);
390             }
391             else
392             {
393                 fprintf (stderr, "W: unknown object: %s\n", iter.key);
394             }
395         }
396         if (method != NULL && strcmp (method, "match") == 0)
397         {
398             FcPattern *match;
399             FcResult res;
400
401             if (!query)
402             {
403                 fprintf (stderr, "E: no query defined.\n");
404                 fail++;
405                 goto bail;
406             }
407             if (!result)
408             {
409                 fprintf (stderr, "E: no result defined.\n");
410                 fail++;
411                 goto bail;
412             }
413             FcConfigSubstitute (config, query, FcMatchPattern);
414             FcDefaultSubstitute (query);
415             match = FcFontMatch (config, query, &res);
416             if (match)
417             {
418                 FcPatternIter iter;
419                 int x, vc;
420
421                 FcPatternIterStart (result, &iter);
422                 do
423                 {
424                     vc = FcPatternIterValueCount (result, &iter);
425                     for (x = 0; x < vc; x++)
426                     {
427                         FcValue vr, vm;
428
429                         if (FcPatternIterGetValue (result, &iter, x, &vr, NULL) != FcResultMatch)
430                         {
431                             fprintf (stderr, "E: unable to obtain a value from the expected result\n");
432                             fail++;
433                             goto bail;
434                         }
435                         if (FcPatternGet (match, FcPatternIterGetObject (result, &iter), x, &vm) != FcResultMatch)
436                         {
437                             vm.type = FcTypeVoid;
438                         }
439                         if (!FcValueEqual (vm, vr))
440                         {
441                             printf ("E: failed to compare %s:\n", FcPatternIterGetObject (result, &iter));
442                             printf ("   actual result:");
443                             FcValuePrint (vm);
444                             printf ("\n   expected result:");
445                             FcValuePrint (vr);
446                             printf ("\n");
447                             fail++;
448                             goto bail;
449                         }
450                     }
451                 } while (FcPatternIterNext (result, &iter));
452             bail:
453                 FcPatternDestroy (match);
454             }
455             else
456             {
457                 fprintf (stderr, "E: no match\n");
458                 fail++;
459             }
460         }
461         else if (method != NULL && strcmp (method, "list") == 0)
462         {
463             FcFontSet *fs;
464
465             if (!query)
466             {
467                 fprintf (stderr, "E: no query defined.\n");
468                 fail++;
469                 goto bail2;
470             }
471             if (!result_fs)
472             {
473                 fprintf (stderr, "E: no result_fs defined.\n");
474                 fail++;
475                 goto bail2;
476             }
477             fs = FcFontList (config, query, NULL);
478             if (!fs)
479             {
480                 fprintf (stderr, "E: failed on FcFontList\n");
481                 fail++;
482             }
483             else
484             {
485                 int i;
486
487                 if (fs->nfont != result_fs->nfont)
488                 {
489                     printf ("E: The number of results is different:\n");
490                     printf ("   actual result: %d\n", fs->nfont);
491                     printf ("   expected result: %d\n", result_fs->nfont);
492                     fail++;
493                     goto bail2;
494                 }
495                 for (i = 0; i < fs->nfont; i++)
496                 {
497                     FcPatternIter iter;
498                     int x, vc;
499
500                     FcPatternIterStart (result_fs->fonts[i], &iter);
501                     do
502                     {
503                         vc = FcPatternIterValueCount (result_fs->fonts[i], &iter);
504                         for (x = 0; x < vc; x++)
505                         {
506                             FcValue vr, vm;
507
508                             if (FcPatternIterGetValue (result_fs->fonts[i], &iter, x, &vr, NULL) != FcResultMatch)
509                             {
510                                 fprintf (stderr, "E: unable to obtain a value from the expected result\n");
511                                 fail++;
512                                 goto bail2;
513                             }
514                             if (FcPatternGet (fs->fonts[i], FcPatternIterGetObject (result_fs->fonts[i], &iter), x, &vm) != FcResultMatch)
515                             {
516                                 vm.type = FcTypeVoid;
517                             }
518                             if (!FcValueEqual (vm, vr))
519                             {
520                                 printf ("E: failed to compare %s:\n", FcPatternIterGetObject (result_fs->fonts[i], &iter));
521                                 printf ("   actual result:");
522                                 FcValuePrint (vm);
523                                 printf ("\n   expected result:");
524                                 FcValuePrint (vr);
525                                 printf ("\n");
526                                 fail++;
527                                 goto bail2;
528                             }
529                         }
530                     } while (FcPatternIterNext (result_fs->fonts[i], &iter));
531                 }
532             bail2:
533                 FcFontSetDestroy (fs);
534             }
535         }
536         else
537         {
538             fprintf (stderr, "W: unknown testing method: %s\n", method);
539         }
540         if (method)
541             method = NULL;
542         if (result)
543         {
544             FcPatternDestroy (result);
545             result = NULL;
546         }
547         if (result_fs)
548         {
549             FcFontSetDestroy (result_fs);
550             result_fs = NULL;
551         }
552         if (query)
553         {
554             FcPatternDestroy (query);
555             query = NULL;
556         }
557     }
558
559     return fail == 0;
560 }
561
562 static FcBool
563 run_scenario (FcConfig *config, char *file)
564 {
565     FcBool ret = FcTrue;
566     json_object *root, *scenario;
567
568     root = json_object_from_file (file);
569     if (!root)
570     {
571         fprintf (stderr, "E: Unable to read the file: %s\n", file);
572         return FcFalse;
573     }
574     if (!build_fonts (config, root))
575     {
576         ret = FcFalse;
577         goto bail1;
578     }
579     if (!run_test (config, root))
580     {
581         ret = FcFalse;
582         goto bail1;
583     }
584
585 bail1:
586     json_object_put (root);
587
588     return ret;
589 }
590
591 static FcBool
592 load_config (FcConfig *config, char *file)
593 {
594     FILE *fp;
595     long len;
596     char *buf = NULL;
597     FcBool ret = FcTrue;
598
599     if ((fp = fopen(file, "rb")) == NULL)
600         return FcFalse;
601     fseek (fp, 0L, SEEK_END);
602     len = ftell (fp);
603     fseek (fp, 0L, SEEK_SET);
604     buf = malloc (sizeof (char) * (len + 1));
605     if (!buf)
606     {
607         ret = FcFalse;
608         goto bail1;
609     }
610     fread (buf, (size_t)len, sizeof (char), fp);
611     buf[len] = 0;
612
613     ret = FcConfigParseAndLoadFromMemory (config, (const FcChar8 *) buf, FcTrue);
614 bail1:
615     fclose (fp);
616     if (buf)
617         free (buf);
618
619     return ret;
620 }
621
622 int
623 main (int argc, char **argv)
624 {
625     FcConfig *config;
626     int retval = 0;
627
628     if (argc < 3)
629     {
630         fprintf(stderr, "Usage: %s <conf file> <test scenario>\n", argv[0]);
631         return 1;
632     }
633
634     config = FcConfigCreate ();
635     if (!load_config (config, argv[1]))
636     {
637         fprintf(stderr, "E: Failed to load config\n");
638         retval = 1;
639         goto bail1;
640     }
641     if (!run_scenario (config, argv[2]))
642     {
643         retval = 1;
644         goto bail1;
645     }
646 bail1:
647     FcConfigDestroy (config);
648
649     return retval;
650 }