1 // vim: ts=2 sw=2 et
2 /*
3 * These tests are of limited usefulness. In fact, you might even say that
4 * they're not really tests at all. But I felt that it would be useful to have
5 * some basic usage of most functions just to verify that things compile and
6 * work generally
7 */
8
9 #include <cfloat>
10 #include <stdexcept>
11 #include <boost/test/unit_test.hpp>
12 #include <boost/test/test_tools.hpp>
13 #include <boost/test/floating_point_comparison.hpp>
14 using namespace boost::unit_test;
15 #include <cairomm/fontface.h>
16 #include <cairomm/scaledfont.h>
17 #include <cairomm/surface.h>
18 #include <cairomm/context.h>
19
20 using namespace Cairo;
21
22 // little utility helper classes
23 struct TestSetup
24 {
TestSetupTestSetup25 TestSetup()
26 {
27 surface = ImageSurface::create(Cairo::FORMAT_ARGB32, 100, 100);
28 cr = Cairo::Context::create(surface);
29 }
30
31 RefPtr<Context> cr;
32 RefPtr<Surface> surface;
33 };
34
35 // a no-op-render user font base class
36 class NullRenderUserFont : public UserFontFace
37 {
38 public:
39 ErrorStatus
render_glyph(const RefPtr<ScaledFont> &,unsigned long,const RefPtr<Context> &,TextExtents &)40 render_glyph(const RefPtr<ScaledFont>& /*scaled_font*/,
41 unsigned long /*glyph*/,
42 const RefPtr<Context>& /*cr*/,
43 TextExtents& /*metrics*/)
44 { ++count_render_glyph; return CAIRO_STATUS_SUCCESS; }
45
46 int count_render_glyph;
47
48 protected:
NullRenderUserFont()49 NullRenderUserFont() : UserFontFace(), count_render_glyph(0) {}
50 };
51
52 /******************************
53 * test_implement_text
54 ******************************/
55 class ImplTextUserFont: public NullRenderUserFont
56 {
57 public:
create()58 static RefPtr<ImplTextUserFont> create() { return RefPtr<ImplTextUserFont>(new ImplTextUserFont());};
text_to_glyphs(const RefPtr<ScaledFont> &,const std::string &,std::vector<Glyph> & glyphs,std::vector<TextCluster> &,TextClusterFlags &)59 ErrorStatus text_to_glyphs(const RefPtr<ScaledFont>& /*scaled_font*/,
60 const std::string& /*utf8*/,
61 std::vector<Glyph>& glyphs,
62 std::vector<TextCluster>& /*clusters*/,
63 TextClusterFlags& /*cluster_flags*/) override
64 {
65 ++count_text_to_glyphs;
66 // return an arbitrary glyph
67 Glyph g = {84, 0, 0};
68 glyphs.push_back(g);
69 return CAIRO_STATUS_SUCCESS;
70 }
71 int count_text_to_glyphs;
72
73 protected:
ImplTextUserFont()74 ImplTextUserFont() : count_text_to_glyphs(0) {}
75 };
76
test_implement_text()77 void test_implement_text()
78 {
79 TestSetup setup;
80 auto font = ImplTextUserFont::create();
81 setup.cr->set_font_face(font);
82 setup.cr->show_text("hello");
83 BOOST_REQUIRE(font->count_text_to_glyphs > 0);
84 BOOST_REQUIRE(font->count_render_glyph > 0);
85 }
86
87 /******************************
88 * test_implement_unicode
89 ******************************/
90 class ImplUnicodeUserFont: public NullRenderUserFont
91 {
92 public:
create()93 static RefPtr<ImplUnicodeUserFont> create() { return RefPtr<ImplUnicodeUserFont>(new ImplUnicodeUserFont());};
unicode_to_glyph(const RefPtr<ScaledFont> &,unsigned long,unsigned long &)94 ErrorStatus unicode_to_glyph(const RefPtr<ScaledFont>& /*scaled_font*/,
95 unsigned long /*unicode*/,
96 unsigned long& /*glyph*/) override
97 { ++count_unicode_to_glyph; return CAIRO_STATUS_SUCCESS;}
98 int count_unicode_to_glyph;
99
100 protected:
ImplUnicodeUserFont()101 ImplUnicodeUserFont() : NullRenderUserFont(), count_unicode_to_glyph(0) {}
102 };
103
test_implement_unicode()104 void test_implement_unicode()
105 {
106 TestSetup setup;
107 auto font = ImplTextUserFont::create();
108 setup.cr->set_font_face(font);
109 setup.cr->show_text("hello");
110 BOOST_REQUIRE(font->count_text_to_glyphs > 0);
111 BOOST_REQUIRE(font->count_render_glyph > 0);
112 }
113
114 /******************************
115 * test_implement_both
116 ******************************/
117 class ImplBothUserFont: public NullRenderUserFont
118 {
119 public:
create()120 static RefPtr<ImplBothUserFont> create() { return RefPtr<ImplBothUserFont>(new ImplBothUserFont());};
unicode_to_glyph(const RefPtr<ScaledFont> &,unsigned long,unsigned long &)121 ErrorStatus unicode_to_glyph(const RefPtr<ScaledFont>& /*scaled_font*/,
122 unsigned long /*unicode*/,
123 unsigned long& /*glyph*/) override
124 { ++count_unicode_to_glyph; return CAIRO_STATUS_SUCCESS;}
125 int count_unicode_to_glyph;
126
text_to_glyphs(const RefPtr<ScaledFont> &,const std::string &,std::vector<Glyph> & glyphs,std::vector<TextCluster> &,TextClusterFlags &)127 ErrorStatus text_to_glyphs(const RefPtr<ScaledFont>& /*scaled_font*/,
128 const std::string& /*utf8*/,
129 std::vector<Glyph>& glyphs,
130 std::vector<TextCluster>& /*clusters*/,
131 TextClusterFlags& /*cluster_flags*/) override
132 {
133 ++count_text_to_glyphs;
134 // return an arbitrary glyph
135 Glyph g = {84, 0, 0};
136 glyphs.push_back(g);
137 return CAIRO_STATUS_SUCCESS;
138 }
139 int count_text_to_glyphs;
140
141 protected:
ImplBothUserFont()142 ImplBothUserFont() : NullRenderUserFont(), count_unicode_to_glyph(0),
143 count_text_to_glyphs(0) {}
144 };
145
test_implement_both()146 void test_implement_both()
147 {
148 TestSetup setup;
149 auto font = ImplBothUserFont::create();
150 setup.cr->set_font_face(font);
151 setup.cr->show_text("hello");
152 // text_to_glyphs should take precedence
153 BOOST_REQUIRE(font->count_text_to_glyphs > 0);
154 BOOST_REQUIRE(font->count_unicode_to_glyph == 0);
155 BOOST_REQUIRE(font->count_render_glyph > 0);
156 }
157
158 /******************************
159 * test_implement_neither
160 ******************************/
161 class ImplNeitherUserFont: public NullRenderUserFont
162 {
163 public:
create()164 static RefPtr<ImplNeitherUserFont> create() { return RefPtr<ImplNeitherUserFont>(new ImplNeitherUserFont());};
165
166 protected:
ImplNeitherUserFont()167 ImplNeitherUserFont() : NullRenderUserFont() {}
168 };
169
test_implement_neither()170 void test_implement_neither()
171 {
172 TestSetup setup;
173 auto font = ImplNeitherUserFont::create();
174 setup.cr->set_font_face(font);
175 setup.cr->show_text("hello");
176 BOOST_REQUIRE(font->count_render_glyph > 0);
177 }
178
179 /******************************
180 * test_implement_init
181 ******************************/
182 class ImplInitUserFont: public NullRenderUserFont
183 {
184 public:
create()185 static RefPtr<ImplInitUserFont> create() { return RefPtr<ImplInitUserFont>(new ImplInitUserFont());};
init(const RefPtr<ScaledFont> &,const RefPtr<Context> &,FontExtents &)186 ErrorStatus init(const RefPtr<ScaledFont>& /*scaled_font*/,
187 const RefPtr<Context>& /*cr*/,
188 FontExtents& /*extents*/) override
189 {++count_init; return CAIRO_STATUS_SUCCESS;}
190
191 int count_init;
192
193 protected:
ImplInitUserFont()194 ImplInitUserFont() : NullRenderUserFont(), count_init(0) {}
195 };
196
test_implement_init()197 void test_implement_init()
198 {
199 TestSetup setup;
200 auto font = ImplInitUserFont::create();
201 setup.cr->set_font_face(font);
202 setup.cr->show_text("hello");
203 BOOST_REQUIRE(font->count_init > 0);
204 BOOST_REQUIRE(font->count_render_glyph > 0);
205 }
206
207 class ExceptionUserFont : public UserFontFace
208 {
209 public:
create(int flags)210 static RefPtr<ExceptionUserFont> create(int flags) { return RefPtr<ExceptionUserFont>(new ExceptionUserFont(flags));};
211
212 ErrorStatus
render_glyph(const RefPtr<ScaledFont> &,unsigned long,const RefPtr<Context> &,TextExtents &)213 render_glyph(const RefPtr<ScaledFont>& /*scaled_font*/,
214 unsigned long /*glyph*/,
215 const RefPtr<Context>& /*cr*/,
216 TextExtents& /*metrics*/)
217 {
218 count_render_glyph++;
219 if (m_flags & FLAG_RENDER)
220 throw std::logic_error("render-glyph exception");
221 return CAIRO_STATUS_SUCCESS;
222 }
223
224 ErrorStatus
unicode_to_glyph(const RefPtr<ScaledFont> &,unsigned long unicode,unsigned long & glyph)225 unicode_to_glyph(const RefPtr<ScaledFont>& /*scaled_font*/,
226 unsigned long unicode,
227 unsigned long& glyph)
228 {
229 count_unicode_to_glyph++;
230 if (m_flags & FLAG_UNICODE)
231 throw std::logic_error("unicode-to-glyph exception");
232
233 glyph = unicode;
234 return CAIRO_STATUS_SUCCESS;
235 }
236
237 ErrorStatus
init(const RefPtr<ScaledFont> &,const RefPtr<Context> &,FontExtents &)238 init(const RefPtr<ScaledFont>& /*scaled_font*/,
239 const RefPtr<Context>& /*cr*/,
240 FontExtents& /*extents*/) override
241 {
242 count_init++;
243 if (m_flags & FLAG_INIT)
244 throw std::logic_error("init exception");
245 return CAIRO_STATUS_SUCCESS;
246 }
247
248 int count_render_glyph;
249 int count_text_to_glyphs;
250 int count_unicode_to_glyph;
251 int count_init;
252 int m_flags;
253
254 static const int FLAG_INIT = 1 << 0;
255 static const int FLAG_UNICODE = 1 << 1;
256 static const int FLAG_RENDER = 1 << 2;
257
258 protected:
ExceptionUserFont(int flags)259 ExceptionUserFont(int flags) : UserFontFace(), count_render_glyph(0),
260 count_text_to_glyphs(0), count_unicode_to_glyph(0), count_init(0),
261 m_flags(flags) {}
262 };
263
test_user_font_exception()264 void test_user_font_exception()
265 {
266 auto font =
267 ExceptionUserFont::create(ExceptionUserFont::FLAG_INIT);
268 BOOST_CHECK(font);
269
270 // the init() callback will throw an exception, if this isn't handled in the
271 // callback wrapper, the program will abort since an exception can't unwind
272 // through C code. However, due to the exception being thrown, the create()
273 // function will fail and throw a new exception. So if the executable doesn't
274 // abort, we should get an exception here.
275 Cairo::RefPtr<Cairo::ScaledFont> scaled_font;
276 BOOST_CHECK_THROW (scaled_font = Cairo::ScaledFont::create(font,
277 Cairo::scaling_matrix(10, 10),
278 Cairo::identity_matrix(),
279 Cairo::FontOptions()),
280 Cairo::logic_error);
281 BOOST_CHECK (font->count_init > 0);
282
283 // now test when an exception is thrown in unicode_to_glyph
284 font = ExceptionUserFont::create(ExceptionUserFont::FLAG_UNICODE);
285 BOOST_CHECK_NO_THROW (scaled_font = Cairo::ScaledFont::create(font,
286 Cairo::scaling_matrix(10, 10),
287 Cairo::identity_matrix(),
288 Cairo::FontOptions()));
289 TestSetup setup;
290 setup.cr->set_font_face(font);
291 // this call should throw an exception since the callback wrapper will return
292 // an error status (that will be translated into an exception) but the test
293 // shouldn't abort since the callback exceptions are handled by the callback
294 // wrapper
295 BOOST_REQUIRE_EQUAL (CAIRO_STATUS_SUCCESS, font->get_status());
296 BOOST_CHECK_THROW(setup.cr->show_text("Hello, world"), Cairo::logic_error);
297 BOOST_CHECK(font->count_unicode_to_glyph > 0);
298 BOOST_CHECK_EQUAL(font->count_render_glyph, 0);
299
300 // now test when an exception is thrown in render_glyph
301 font = ExceptionUserFont::create(ExceptionUserFont::FLAG_RENDER);
302 BOOST_CHECK_NO_THROW (scaled_font = Cairo::ScaledFont::create(font,
303 Cairo::scaling_matrix(10, 10),
304 Cairo::identity_matrix(),
305 Cairo::FontOptions()));
306 // need a new setup since the old cr is now in an error state, so attemtping
307 // to use it will throw an exception
308 TestSetup setup2;
309 BOOST_CHECK_NO_THROW(setup2.cr->set_font_face(font));
310 BOOST_REQUIRE_EQUAL (CAIRO_STATUS_SUCCESS, font->get_status());
311 BOOST_CHECK_THROW(setup2.cr->show_text("Hello, world"), Cairo::logic_error);
312 BOOST_CHECK (font->count_unicode_to_glyph > 0);
313 BOOST_CHECK (font->count_render_glyph > 0);
314 }
315
316
317 test_suite*
init_unit_test_suite(int argc,char * argv[])318 init_unit_test_suite(int argc, char* argv[])
319 {
320 // compile even with -Werror
321 if (argc && argv) {}
322
323 test_suite* test= BOOST_TEST_SUITE( "Cairo::UserFontFace Tests" );
324
325 test->add (BOOST_TEST_CASE (&test_implement_text));
326 test->add (BOOST_TEST_CASE (&test_implement_unicode));
327 test->add (BOOST_TEST_CASE (&test_implement_both));
328 test->add (BOOST_TEST_CASE (&test_implement_neither));
329 test->add (BOOST_TEST_CASE (&test_implement_init));
330 test->add (BOOST_TEST_CASE (&test_user_font_exception));
331
332 return test;
333 }
334