Git init
[external/pango1.0.git] / tests / testboundaries_ucd.c
1 /* Pango
2  * testboundaries_ucd.c: Test text boundary algorithms with test data from
3  *                       Unicode Character Database.
4  *
5  * Copyright (C) 2003 Noah Levitt
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include <pango/pango.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <locale.h>
27
28 static gboolean failed = FALSE;
29
30 /* PangoLogAttr has to be the same size as guint or this hack breaks */
31 typedef union
32 {
33   PangoLogAttr attr;
34   guint bits;
35 }
36 AttrBits;
37
38 /* counts the number of multiplication and divison signs up to the first
39  * '#' or null character */
40 static gint
41 count_attrs (gchar *line)
42 {
43   gunichar ch;
44   gchar *p = line;
45   gint count = 0;
46
47   for (;;)
48     {
49       ch = g_utf8_get_char (p);
50
51       switch (ch)
52         {
53         /* MULTIPLICATION SIGN, DIVISION SIGN */
54         case 0x00d7: case 0x00f7:
55           count++;
56           break;
57
58         /* null char, NUMBER SIGN */
59         case 0x0000: case 0x0023:
60           return count;
61
62         default:
63           break;
64         }
65
66       p = g_utf8_next_char (p);
67     }
68   /* not reached */
69 }
70
71 static gboolean
72 parse_line (gchar *line,
73             AttrBits bits,
74             gchar **str_return,
75             PangoLogAttr **attr_return,
76             gint *num_attrs)
77 {
78   GString *gs;
79   gunichar ch, character;
80   gchar *p, *q;
81   gint i;
82   AttrBits temp_attr;
83
84   *num_attrs = count_attrs (line);
85   *attr_return = g_new (PangoLogAttr, *num_attrs);
86
87   p = line;
88   i = 0;
89   gs = g_string_new (NULL);
90
91   for (;;)
92     {
93       temp_attr.bits = 0;
94
95       /* skip white space */
96       do
97         {
98           ch = g_utf8_get_char (p);
99           p = g_utf8_next_char (p);
100         }
101       while (g_unichar_isspace (ch));
102
103       switch (ch)
104         {
105         case 0x00f7: /* DIVISION SIGN: boundary here */
106           temp_attr.bits |= bits.bits;
107           /* fall through */
108
109         case 0x00d7: /* MULTIPLICATION SIGN: no boundary here */
110           break;
111
112         case 0x0000:
113         case 0x0023: 
114           *str_return = g_string_free (gs, FALSE);
115           return TRUE;
116
117         default: /* unexpected character */
118           g_free (*attr_return);
119           return FALSE;
120         }
121
122       (*attr_return)[i] = temp_attr.attr;
123
124       /* skip white space */
125       do
126         {
127           ch = g_utf8_get_char (p);
128           p = g_utf8_next_char (p);
129         }
130       while (g_unichar_isspace (ch));
131       p = g_utf8_prev_char (p);
132
133       if (ch == 0x0023 || ch == 0x0000)
134         {
135           *str_return = g_string_free (gs, FALSE);
136           return TRUE;
137         }
138
139       character = strtoul (p, &q, 16);
140       if (q < p + 4 || q > p + 6 || character > 0x10ffff)
141         {
142           g_free (*attr_return);
143           return FALSE;
144         }
145
146       p = q;
147
148       gs = g_string_append_unichar (gs, character);
149
150       i++;
151     }
152 }
153
154 static gboolean
155 attrs_equal (PangoLogAttr *attrs1,
156              PangoLogAttr *attrs2,
157              gint len,
158              AttrBits bits)
159 {
160   AttrBits a, b;
161   gint i;
162
163   for (i = 0;  i < len;  i++)
164     {
165       a.bits = 0;
166       a.attr = attrs1[i];
167
168       b.bits = 0;
169       b.attr = attrs2[i];
170
171       /* can't do a straight comparison because the bitmask may have
172        * multiple bits set, and as long as attr&bitmask is not zero, it
173        * counts as being set */
174       if (((a.bits & bits.bits) && !(b.bits & bits.bits)) ||
175           !(a.bits & bits.bits) && (b.bits & bits.bits))
176         return FALSE;
177     }
178
179   return TRUE;
180 }
181
182 static gchar *
183 make_test_string (gchar *string, 
184                   PangoLogAttr *attrs, 
185                   AttrBits bits)
186 {
187   GString *gs = g_string_new (NULL);
188   gint i = 0;
189   AttrBits a;
190   gchar *p = string;
191   gunichar ch;
192
193   for (;;)
194     {
195       a.bits = 0;
196       a.attr = attrs[i];
197       if ((a.bits & bits.bits) != 0)
198         gs = g_string_append_unichar (gs, 0x00f7);
199       else
200         gs = g_string_append_unichar (gs, 0x00d7);
201
202       g_string_append_c (gs, ' ');
203
204       if (*p == '\0')
205         break;
206
207       ch = g_utf8_get_char (p);
208       g_string_append_printf (gs, "%04X ", ch);
209
210       p = g_utf8_next_char (p);
211       i++;
212     }
213
214   return g_string_free (gs, FALSE);
215 }
216
217 static void
218 do_test (gchar *filename,
219          AttrBits bits,
220          gboolean fixup_broken_linebreaktest)
221 {
222   GIOChannel *channel;
223   GIOStatus status;
224   gchar *line;
225   gsize length, terminator_pos;
226   GError *error;
227   gchar *string;
228   PangoLogAttr *expected_attrs;
229   gint num_attrs;
230   gint i;
231
232   error = NULL;
233   channel = g_io_channel_new_file (filename, "r", &error);
234   if (!channel)
235     {
236       if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT)
237         {
238           g_print ("%s not found.  Skipping test.\n", filename);
239           goto done;
240         }
241       else
242         {
243           g_printerr ("%s: %s\n", filename, error->message);
244           exit (1);
245         }
246     }
247
248   i = 1;
249   for (;;)
250     {
251       error = NULL;
252       status = g_io_channel_read_line (channel, &line, &length, &terminator_pos, &error);
253
254       switch (status)
255         {
256           case G_IO_STATUS_ERROR:
257             g_printerr ("%s: %s\n", filename, error->message);
258             exit (1);
259
260           case G_IO_STATUS_EOF:
261             goto done;
262
263           case G_IO_STATUS_AGAIN:
264             continue;
265
266           case G_IO_STATUS_NORMAL:
267             line[terminator_pos] = '\0';
268             break;
269         }
270
271       if (! parse_line (line, bits, &string, &expected_attrs, &num_attrs))
272         {
273           g_printerr ("%s: error parsing line %d: %s\n", filename, i, line);
274           exit (1);
275         }
276       
277       if (num_attrs > 0)
278         {
279           PangoLogAttr *attrs = g_new (PangoLogAttr, num_attrs);
280           pango_get_log_attrs (string, -1, 0, pango_language_from_string ("C"), attrs, num_attrs);
281
282           /* LineBreakTest.txt from Unicode 5.1.0 has this bug that it says
283            * breaking is allowed at the beginning of the strings, while the
284            * algorithm says it's not.  Fix that up. */
285           if (fixup_broken_linebreaktest)
286             memset (expected_attrs, 0, sizeof (expected_attrs[0]));
287
288           if (! attrs_equal (attrs, expected_attrs, num_attrs, bits))
289             {
290               gchar *str = make_test_string (string, attrs, bits);
291               gchar *comments = strchr (line, '#');
292               if (comments) /* don't print the # comment in the error message.  print it separately */
293                 {
294                   *comments = '\0';
295                   comments++;
296                 }
297               else
298                 {
299                   comments = "";
300                 }
301
302               g_printerr ("%s: line %d failed\n"
303                           "   expected: %s\n"
304                           "   returned: %s\n"
305                           "   comments: %s\n\n",
306                           filename, i, line, str, comments);
307
308               g_free (str);
309               failed = TRUE;
310             }
311           g_free (attrs);
312         }
313       g_free (string);
314       g_free (expected_attrs);
315
316       i++;
317     }
318
319 done:
320   if (channel)
321     g_io_channel_unref (channel);
322   if (error)
323     g_error_free (error);
324   g_free (filename);
325 }
326
327 gint
328 main (gint argc,
329       gchar **argv)
330 {
331   gchar *srcdir;
332   gchar *filename;
333   AttrBits bits;
334
335   setlocale (LC_ALL, "");
336
337   srcdir = getenv ("srcdir");
338   if (!srcdir)
339     srcdir = ".";
340
341   filename = g_strdup_printf ("%s/GraphemeBreakTest.txt", srcdir);
342   bits.bits = 0;
343   bits.attr.is_cursor_position = 1;
344   do_test (filename, bits, FALSE);
345
346   filename = g_strdup_printf ("%s/WordBreakTest.txt", srcdir);
347   bits.bits = 0;
348   bits.attr.is_word_boundary = 1;
349   do_test (filename, bits, FALSE);
350
351   filename = g_strdup_printf ("%s/SentenceBreakTest.txt", srcdir);
352   bits.bits = 0;
353   bits.attr.is_sentence_boundary = 1;
354   do_test (filename, bits, FALSE);
355
356   filename = g_strdup_printf ("%s/LineBreakTest.txt", srcdir);
357   bits.bits = 0;
358   bits.attr.is_line_break = 1;
359   bits.attr.is_mandatory_break = 1;
360   do_test (filename, bits, TRUE);
361
362   exit (failed);
363 }