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