test-conf.c revision a4e54154
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 *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 45static FcPattern * 46build_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 281static FcFontSet * 282build_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 302static FcBool 303build_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 323static FcBool 324run_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 562static FcBool 563run_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 585bail1: 586 json_object_put (root); 587 588 return ret; 589} 590 591static FcBool 592load_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); 614bail1: 615 fclose (fp); 616 if (buf) 617 free (buf); 618 619 return ret; 620} 621 622int 623main (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 } 646bail1: 647 FcConfigDestroy (config); 648 649 return retval; 650} 651