1/*
2 * fontconfig/test/test-family-matching.c
3 *
4 * Copyright © 2020 Zoltan Vandrus
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of the author(s) not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission.  The authors make no
13 * representations about the suitability of this software for any purpose.  It
14 * is provided "as is" without express or implied warranty.
15 *
16 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 * PERFORMANCE OF THIS SOFTWARE.
23 */
24#include <stdio.h>
25#include <stdlib.h>
26#include <fontconfig/fontconfig.h>
27
28#define FC_TEST_RESULT "testresult"
29
30typedef enum _TestMatchResult {
31    TestMatch,
32    TestNoMatch,
33    TestMatchError
34} TestMatchResult;
35
36typedef enum _TestResult {
37    TestPassed,
38    TestFailed,
39    TestError
40} TestResult;
41
42static TestMatchResult
43TestMatchPattern (const char *test, FcPattern *p)
44{
45    const FcChar8 *xml_pre = (const FcChar8 *) ""
46        "<fontconfig>\n"
47        "  <match>\n"
48        "";
49
50    const FcChar8 *xml_post = (const FcChar8 *) ""
51        "    <edit name=\""FC_TEST_RESULT"\">\n"
52        "      <bool>true</bool>\n"
53        "    </edit>\n"
54        "  </match>\n"
55        "</fontconfig>\n"
56        "";
57
58    FcPattern *pat = NULL;
59    FcChar8 *concat = NULL;
60    FcChar8 *xml = NULL;
61    FcConfig *cfg = NULL;
62    FcResult result;
63    FcBool dummy;
64    TestMatchResult ret = TestMatchError;
65
66    pat = FcPatternDuplicate (p);
67    if (!pat)
68    {
69        fprintf (stderr, "Unable to duplicate pattern.\n");
70        goto bail;
71    }
72
73    concat = FcStrPlus (xml_pre, (const FcChar8 *) test);
74    if (!concat)
75    {
76        fprintf (stderr, "Concatenation failed.\n");
77        goto bail;
78    }
79
80    xml = FcStrPlus (concat, xml_post);
81    if (!xml)
82    {
83        fprintf (stderr, "Concatenation failed.\n");
84        goto bail;
85    }
86
87    cfg = FcConfigCreate ();
88    if (!cfg)
89    {
90        fprintf (stderr, "Unable to create a new empty config.\n");
91        goto bail;
92    }
93
94    if (!FcConfigParseAndLoadFromMemory (cfg, xml, FcTrue))
95    {
96        fprintf (stderr, "Unable to load a config from memory.\n");
97        goto bail;
98    }
99
100    if (!FcConfigSubstitute (cfg, pat, FcMatchPattern))
101    {
102        fprintf (stderr, "Unable to substitute config.\n");
103        goto bail;
104    }
105
106    result = FcPatternGetBool (pat, FC_TEST_RESULT, 0, &dummy);
107    switch (result) {
108    case FcResultMatch:
109        ret = TestMatch;
110        break;
111    case FcResultNoMatch:
112        ret = TestNoMatch;
113        break;
114    default:
115        fprintf (stderr, "Unable to check pattern.\n");
116        break;
117    }
118
119bail:
120    if (cfg)
121	FcConfigDestroy (cfg);
122    if (xml)
123	FcStrFree (xml);
124    if (concat)
125	FcStrFree (concat);
126    if (pat)
127	FcPatternDestroy (pat);
128    return ret;
129}
130
131static TestResult
132TestShouldMatchPattern(const char* test, FcPattern *pat, int negate)
133{
134    switch (TestMatchPattern (test, pat)) {
135    case TestMatch:
136        if (!negate) {
137            return TestPassed;
138        }
139        else
140        {
141            printf ("Following test unexpectedly matched:\n%s", test);
142            printf ("on\n");
143            FcPatternPrint (pat);
144            return TestFailed;
145        }
146        break;
147    case TestNoMatch:
148        if (!negate) {
149            printf ("Following test should have matched:\n%s", test);
150            printf ("on\n");
151            FcPatternPrint (pat);
152            return TestFailed;
153        }
154        else
155        {
156            return TestPassed;
157        }
158        break;
159    case TestMatchError:
160        return TestError;
161        break;
162    default:
163        fprintf (stderr, "This shouldn't have been reached.\n");
164        return TestError;
165    }
166}
167
168#define MAX(a,b) ((a) > (b) ? (a) : (b))
169
170static TestResult
171UpdateResult (TestResult* res, TestResult resNew)
172{
173    *res = MAX(*res, resNew);
174    return *res;
175}
176
177static TestResult
178TestFamily (void)
179{
180    const char *test;
181    TestResult res = TestPassed;
182
183    FcPattern *pat = FcPatternBuild (NULL,
184        FC_FAMILY, FcTypeString, "family1",
185        FC_FAMILY, FcTypeString, "family2",
186        FC_FAMILY, FcTypeString, "family3",
187        NULL);
188
189    if (!pat)
190    {
191        fprintf (stderr, "Unable to build pattern.\n");
192        return TestError;
193    }
194
195    #define SHOULD_MATCH(p,t) \
196        UpdateResult (&res, TestShouldMatchPattern (t, p, 0))
197    #define SHOULD_NOT_MATCH(p,t) \
198        UpdateResult (&res, TestShouldMatchPattern (t, p, 1))
199
200    test = "<test qual=\"all\" name=\"family\" compare=\"not_eq\">\n"
201           "    <string>foo</string>\n"
202           "</test>\n"
203           "";
204    SHOULD_MATCH(pat, test);
205
206    test = ""
207           "<test qual=\"all\" name=\"family\" compare=\"not_eq\">\n"
208           "    <string>family2</string>\n"
209           "</test>\n"
210           "";
211    SHOULD_NOT_MATCH(pat, test);
212
213    test = ""
214           "<test qual=\"any\" name=\"family\" compare=\"eq\">\n"
215           "    <string>family3</string>\n"
216           "</test>\n"
217           "";
218    SHOULD_MATCH(pat, test);
219
220    test = ""
221           "<test qual=\"any\" name=\"family\" compare=\"eq\">\n"
222           "    <string>foo</string>\n"
223           "</test>\n"
224           "";
225    SHOULD_NOT_MATCH(pat, test);
226
227    FcPatternDestroy (pat);
228    return res;
229}
230
231int
232main (void)
233{
234    return (TestFamily ());
235}
236