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