(root)/
fontconfig-2.14.2/
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  }