1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <getopt.h>
4 
5 #include <check.h>
6 #include <fcft/fcft.h>
7 
8 static struct fcft_font *font = NULL;
9 
10 static void
core_setup(void)11 core_setup(void)
12 {
13     font = fcft_from_name(1, (const char *[]){"Serif"}, NULL);
14     ck_assert_ptr_nonnull(font);
15 }
16 
17 static void
core_teardown(void)18 core_teardown(void)
19 {
20     fcft_destroy(font);
21     font = NULL;
22 }
23 
START_TEST(test_capabilities)24 START_TEST(test_capabilities)
25 {
26     enum fcft_capabilities caps = fcft_capabilities();
27 
28 #if defined(FCFT_HAVE_HARFBUZZ)
29     ck_assert(caps & FCFT_CAPABILITY_GRAPHEME_SHAPING);
30     caps &= ~FCFT_CAPABILITY_GRAPHEME_SHAPING;
31 #endif
32 #if defined(FCFT_HAVE_HARFBUZZ) && defined(FCFT_HAVE_UTF8PROC)
33     ck_assert(caps & FCFT_CAPABILITY_TEXT_RUN_SHAPING);
34     caps &= ~FCFT_CAPABILITY_TEXT_RUN_SHAPING;
35 #endif
36 
37     ck_assert_int_eq(caps, 0);
38 }
39 END_TEST
40 
START_TEST(test_from_name)41 START_TEST(test_from_name)
42 {
43     ck_assert_int_gt(font->height, 0);
44     ck_assert_int_gt(font->max_advance.x, 0);
45     ck_assert_int_gt(font->underline.thickness, 0);
46     ck_assert_int_gt(font->strikeout.thickness, 0);
47 }
48 END_TEST
49 
START_TEST(test_glyph_rasterize)50 START_TEST(test_glyph_rasterize)
51 {
52     const struct fcft_glyph *glyph = fcft_glyph_rasterize(
53         font, L'A', FCFT_SUBPIXEL_NONE);
54     ck_assert_ptr_nonnull(glyph);
55     ck_assert_ptr_nonnull(glyph->pix);
56     ck_assert_int_eq(glyph->wc, L'A');
57     ck_assert_int_eq(glyph->cols, 1);
58     ck_assert_int_gt(glyph->width, 0);
59     ck_assert_int_gt(glyph->height, 0);
60     ck_assert_int_gt(glyph->advance.x, 0);
61 }
62 END_TEST
63 
64 #pragma GCC diagnostic push
65 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
START_TEST(test_size_adjust)66 START_TEST(test_size_adjust)
67 {
68     struct fcft_font *larger = fcft_size_adjust(font, 50.0);
69     ck_assert_ptr_nonnull(larger);
70     ck_assert_int_gt(larger->height, font->height);
71     ck_assert_int_gt(larger->max_advance.x, font->max_advance.x);
72     fcft_destroy(larger);
73 }
74 END_TEST
75 #pragma GCC diagnostic pop
76 
START_TEST(test_precompose)77 START_TEST(test_precompose)
78 {
79     wchar_t ret = fcft_precompose(font, L'a', L'\U00000301', NULL, NULL, NULL);
80 
81     /* All western fonts _should_ have this pre-composed character */
82     ck_assert_int_eq(ret, L'á');
83 
84     /* Can't verify *_is_from_primary since we don't know which font
85      * we're using */
86 
87     ret = fcft_precompose(font, L'X', L'Y', NULL, NULL, NULL);
88     ck_assert_int_eq(ret, (wchar_t)-1);
89 }
90 END_TEST
91 
START_TEST(test_set_scaling_filter)92 START_TEST(test_set_scaling_filter)
93 {
94     ck_assert(fcft_set_scaling_filter(FCFT_SCALING_FILTER_NONE));
95     ck_assert(fcft_set_scaling_filter(FCFT_SCALING_FILTER_NEAREST));
96     ck_assert(fcft_set_scaling_filter(FCFT_SCALING_FILTER_BILINEAR));
97     ck_assert(fcft_set_scaling_filter(FCFT_SCALING_FILTER_CUBIC));
98     ck_assert(fcft_set_scaling_filter(FCFT_SCALING_FILTER_LANCZOS3));
99 
100     ck_assert(!fcft_set_scaling_filter(FCFT_SCALING_FILTER_LANCZOS3 + 120));
101 }
102 END_TEST
103 
104 #if defined(FCFT_HAVE_HARFBUZZ)
105 
106 static struct fcft_font *emoji_font = NULL;
107 
108 static void
text_shaping_setup(void)109 text_shaping_setup(void)
110 {
111     core_setup();
112     emoji_font = fcft_from_name(1, (const char *[]){"emoji"}, NULL);
113     ck_assert_ptr_nonnull(emoji_font);
114 }
115 
116 static void
text_shaping_teardown(void)117 text_shaping_teardown(void)
118 {
119     core_teardown();
120     fcft_destroy(emoji_font);
121     emoji_font = NULL;
122 }
123 
124 
START_TEST(test_emoji_zwj)125 START_TEST(test_emoji_zwj)
126 {
127     const wchar_t *const emoji = L"����";
128     const struct fcft_grapheme *grapheme = fcft_grapheme_rasterize(
129         emoji_font, wcslen(emoji), emoji, 0, NULL, FCFT_SUBPIXEL_DEFAULT);
130     ck_assert_ptr_nonnull(grapheme);
131     ck_assert_int_eq(grapheme->count, 1);
132 
133     /* Verify grapheme was cached */
134     const struct fcft_grapheme *grapheme2 = fcft_grapheme_rasterize(
135         emoji_font, wcslen(emoji), emoji, 0, NULL, FCFT_SUBPIXEL_DEFAULT);
136     ck_assert_ptr_eq(grapheme, grapheme2);
137 }
138 END_TEST
139 #endif
140 
141 Suite *
fcft_suite(bool run_text_shaping_tests)142 fcft_suite(bool run_text_shaping_tests)
143 {
144     Suite *suite = suite_create("fcft");
145 
146     TCase *core = tcase_create("core");
147     tcase_add_checked_fixture(core, &core_setup, &core_teardown);
148 
149     /* Slow systems, like the Pinebook Pro, with a *lot* of fonts, *will* be slow */
150     tcase_set_timeout(core, 60);
151 
152     tcase_add_test(core, test_capabilities);
153     tcase_add_test(core, test_from_name);
154     tcase_add_test(core, test_glyph_rasterize);
155     tcase_add_test(core, test_size_adjust);
156     tcase_add_test(core, test_precompose);
157     tcase_add_test(core, test_set_scaling_filter);
158     suite_add_tcase(suite, core);
159 
160 #if defined(FCFT_HAVE_HARFBUZZ)
161     if (run_text_shaping_tests) {
162         TCase *text_shaping = tcase_create("text-shaping");
163         tcase_set_timeout(text_shaping, 60);
164         tcase_add_checked_fixture(
165             text_shaping, &text_shaping_setup, &text_shaping_teardown);
166         tcase_add_test(text_shaping, test_emoji_zwj);
167         suite_add_tcase(suite, text_shaping);
168     }
169 #endif
170 
171     suite_add_tcase(suite, core);
172     return suite;
173 }
174 
175 static void
print_usage(const char * prog_name)176 print_usage(const char *prog_name)
177 {
178     printf(
179         "Usage: %s [OPTIONS...]\n"
180         "\n"
181         "Options:\n"
182 #if defined(FCFT_HAVE_HARFBUZZ)
183         "  -s,--text-shaping                  run text shaping tests (requires an emoji font to be installed)\n"
184 #endif
185         ,
186         prog_name);
187 }
188 
189 int
main(int argc,char * const * argv)190 main(int argc, char *const *argv)
191 {
192     const char *const prog_name = argv[0];
193 
194     static const struct option longopts[] =  {
195 #if defined(FCFT_HAVE_HARFBUZZ)
196         {"text-shaping", no_argument, NULL, 's'},
197 #endif
198         {NULL,           no_argument, NULL,   0},
199     };
200 
201     bool run_text_shaping_tests = false;
202 
203     while (true) {
204         int c = getopt_long(argc, argv, "sh", longopts, NULL);
205         if (c == -1)
206             break;
207 
208         switch (c) {
209         case 's':
210             run_text_shaping_tests = true;
211             break;
212 
213         case 'h':
214             print_usage(prog_name);
215             return EXIT_SUCCESS;
216 
217         case '?':
218             return EXIT_FAILURE;
219         }
220     }
221 
222     fcft_log_init(FCFT_LOG_COLORIZE_AUTO, false, FCFT_LOG_CLASS_DEBUG);
223 
224     Suite *suite = fcft_suite(run_text_shaping_tests);
225     SRunner *runner = srunner_create(suite);
226 
227     srunner_run_all(runner, CK_NORMAL);
228     int failed = srunner_ntests_failed(runner);
229 
230     srunner_free(runner);
231     return failed;
232 }
233