|
| 1 | +// freedesktop.fontconfig — the three generated tables, exercised through the |
| 2 | +// public API. |
| 3 | +// |
| 4 | +// WHAT CAN BE ASSERTED WITHOUT FONTS |
| 5 | +// |
| 6 | +// This package compiles the four runtime paths EMPTY on purpose (see the |
| 7 | +// manifest), so `FcInit()` on a runner with no `FONTCONFIG_FILE` finds no |
| 8 | +// configuration and no fonts. That is the intended behaviour, not a defect, |
| 9 | +// and it means a font-matching test would only be asserting that the runner |
| 10 | +// has a desktop. |
| 11 | +// |
| 12 | +// The three GENERATED tables need none of that, and they are what this fork |
| 13 | +// exists to produce: |
| 14 | +// |
| 15 | +// fclang.h 281 .orth files compiled into charset bitmaps |
| 16 | +// fccase.h Unicode case folding |
| 17 | +// fcobjshash.h object-name → id, the gperf replacement |
| 18 | +// |
| 19 | +// Each is reachable through a public entry point that takes no config. |
| 20 | + |
| 21 | +#ifdef __linux__ |
| 22 | + |
| 23 | +#include <fontconfig/fontconfig.h> |
| 24 | + |
| 25 | +#include <cstdio> |
| 26 | +#include <cstring> |
| 27 | +#include <initializer_list> |
| 28 | + |
| 29 | +namespace { |
| 30 | + |
| 31 | +int failures = 0; |
| 32 | + |
| 33 | +void check(bool ok, const char *what) |
| 34 | +{ |
| 35 | + std::printf("%-58s %s\n", what, ok ? "ok" : "FAILED"); |
| 36 | + if (!ok) { |
| 37 | + ++failures; |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +} // namespace |
| 42 | + |
| 43 | +int main() |
| 44 | +{ |
| 45 | + std::printf(" FcGetVersion() = %d\n", FcGetVersion()); |
| 46 | + check(FcGetVersion() >= 21500, "FcGetVersion reports 2.15.0 or newer"); |
| 47 | + |
| 48 | + // ── fcobjshash.h — the gperf replacement ───────────────────────────── |
| 49 | + // |
| 50 | + // Exercised through `FcNameParse`, which is the public route into the |
| 51 | + // object table: the parser maps every "name=value" key through |
| 52 | + // `FcObjectLookupIdByName`, which IS the generated lookup. `FcObject` and |
| 53 | + // `FcObjectFromName` are internal (fcint.h), so reaching for them would |
| 54 | + // test the table without testing that a consumer can get to it. |
| 55 | + { |
| 56 | + FcPattern *p = FcNameParse((const FcChar8 *) "Arial:pixelsize=12:slant=100"); |
| 57 | + check(p != nullptr, "fcobjshash: FcNameParse accepted three object names"); |
| 58 | + if (p != nullptr) { |
| 59 | + FcChar8 *fam = nullptr; |
| 60 | + double px = 0; |
| 61 | + int slant = 0; |
| 62 | + const bool gf = FcPatternGetString(p, FC_FAMILY, 0, &fam) == FcResultMatch; |
| 63 | + const bool gp = FcPatternGetDouble(p, FC_PIXEL_SIZE, 0, &px) == FcResultMatch; |
| 64 | + const bool gs = FcPatternGetInteger(p, FC_SLANT, 0, &slant) == FcResultMatch; |
| 65 | + std::printf(" family=%s pixelsize=%g slant=%d\n", |
| 66 | + gf && fam ? (const char *) fam : "(none)", px, slant); |
| 67 | + check(gf && fam && std::strcmp((const char *) fam, "Arial") == 0, |
| 68 | + "…and \"family\" round-tripped to the right object"); |
| 69 | + check(gp && px == 12.0, "…and \"pixelsize\" did too"); |
| 70 | + check(gs && slant == 100, "…and \"slant\" did too"); |
| 71 | + FcPatternDestroy(p); |
| 72 | + } |
| 73 | + |
| 74 | + // A name the table does not contain must not resolve to a builtin — |
| 75 | + // fontconfig assigns it a dynamic id instead. This is the branch a |
| 76 | + // binary search gets wrong when its bounds are off by one. |
| 77 | + FcPattern *q = FcNameParse((const FcChar8 *) ":nosuchproperty=1"); |
| 78 | + check(q != nullptr, "…and an unknown property does not crash the parser"); |
| 79 | + if (q) FcPatternDestroy(q); |
| 80 | + } |
| 81 | + |
| 82 | + // ── fclang.h — the language charsets ───────────────────────────────── |
| 83 | + // `FcLangGetCharSet` indexes straight into fcLangCharSets. Three languages |
| 84 | + // with very different coverage, so a table that was truncated or misordered |
| 85 | + // fails on at least one. |
| 86 | + for (const char *l : {"en", "ru", "zh-cn"}) { |
| 87 | + const FcCharSet *cs = FcLangGetCharSet((const FcChar8 *) l); |
| 88 | + const FcChar32 n = cs ? FcCharSetCount(cs) : 0; |
| 89 | + std::printf(" lang %-6s -> %u codepoints\n", l, n); |
| 90 | + check(cs != nullptr && n > 0, "fclang: the language has a charset"); |
| 91 | + } |
| 92 | + // Latin and Cyrillic must not be the same set — they are separate entries |
| 93 | + // whose leaves were de-duplicated, and a dedup bug shows up here. |
| 94 | + { |
| 95 | + const FcCharSet *en = FcLangGetCharSet((const FcChar8 *) "en"); |
| 96 | + const FcCharSet *ru = FcLangGetCharSet((const FcChar8 *) "ru"); |
| 97 | + check(en && ru && !FcCharSetEqual(en, ru), |
| 98 | + "…and English and Russian are different sets"); |
| 99 | + check(en && FcCharSetHasChar(en, 'A'), "…and English covers 'A'"); |
| 100 | + check(ru && FcCharSetHasChar(ru, 0x0416), "…and Russian covers U+0416"); |
| 101 | + check(en && !FcCharSetHasChar(en, 0x0416), "…and English does not"); |
| 102 | + } |
| 103 | + |
| 104 | + // A language that does not exist must return null rather than index out of |
| 105 | + // range — the fastpath table `fcLangCharSetRanges` is what decides that. |
| 106 | + check(FcLangGetCharSet((const FcChar8 *) "zz-nowhere") == nullptr, |
| 107 | + "fclang: an unknown language returns null"); |
| 108 | + |
| 109 | + // ── fccase.h — Unicode case folding ────────────────────────────────── |
| 110 | + // `FcStrDowncase` walks fcCaseFold. ASCII exercises the EVEN_ODD/RANGE |
| 111 | + // methods; U+0130 (Latin capital I with dot) is a FULL fold — one char to |
| 112 | + // several — which is the method with its own byte pool. |
| 113 | + { |
| 114 | + FcChar8 *d = FcStrDowncase((const FcChar8 *) "ABC"); |
| 115 | + std::printf(" downcase(\"ABC\") = %s\n", d ? (const char *) d : "(null)"); |
| 116 | + check(d && std::strcmp((const char *) d, "abc") == 0, |
| 117 | + "fccase: ASCII folds through the range methods"); |
| 118 | + if (d) FcStrFree(d); |
| 119 | + } |
| 120 | + { |
| 121 | + // U+0410 CYRILLIC CAPITAL A -> U+0430, a two-byte fold. |
| 122 | + FcChar8 *d = FcStrDowncase((const FcChar8 *) "\xd0\x90"); |
| 123 | + const bool ok = d && std::strcmp((const char *) d, "\xd0\xb0") == 0; |
| 124 | + std::printf(" downcase(U+0410) = %s\n", ok ? "U+0430" : "(wrong)"); |
| 125 | + check(ok, "…and a Cyrillic capital folds to its lowercase"); |
| 126 | + if (d) FcStrFree(d); |
| 127 | + } |
| 128 | + |
| 129 | + std::printf("\n%d check(s) failed\n", failures); |
| 130 | + return failures == 0 ? 0 : 1; |
| 131 | +} |
| 132 | + |
| 133 | +#else |
| 134 | +int main() { return 0; } |
| 135 | +#endif |
0 commit comments