Simplify make install
[yaz-moved-to-github.git] / test / test_icu.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2013 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /* DO NOT EDIT THIS FILE IF YOUR EDITOR DOES NOT SUPPORT UTF-8 */
7
8
9 #if HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #define USE_TIMING 0
14 #if USE_TIMING
15 #include <yaz/timing.h>
16 #endif
17
18 #include <yaz/test.h>
19 #include <yaz/log.h>
20 #include <yaz/wrbuf.h>
21
22 #if YAZ_HAVE_ICU
23 #include <yaz/icu_I18N.h>
24 #include <unicode/uclean.h>
25
26 #if YAZ_POSIX_THREADS
27 #include <pthread.h>
28 #endif
29
30 #if YAZ_HAVE_XML2
31 #include <libxml/xmlmemory.h>
32 #endif
33
34 #include <string.h>
35 #include <stdlib.h>
36
37 #define MAX_KEY_SIZE 256
38 struct icu_termmap
39 {
40     uint8_t sort_key[MAX_KEY_SIZE]; /* standard C string '\0' terminated */
41     char disp_term[MAX_KEY_SIZE];  /* standard C utf-8 string */
42 };
43
44
45 static int icu_termmap_cmp(const void *vp1, const void *vp2)
46 {
47     struct icu_termmap *itmp1 = *(struct icu_termmap **) vp1;
48     struct icu_termmap *itmp2 = *(struct icu_termmap **) vp2;
49
50     int cmp = 0;
51
52     cmp = strcmp((const char *)itmp1->sort_key,
53                  (const char *)itmp2->sort_key);
54     return cmp;
55 }
56
57
58 static int test_icu_casemap(const char *locale, char action,
59                             const char *src8cstr, const char *chk8cstr)
60 {
61     int success = 0;
62     UErrorCode status = U_ZERO_ERROR;
63
64     struct icu_buf_utf8 *src8 = icu_buf_utf8_create(0);
65     struct icu_buf_utf8 *dest8 = icu_buf_utf8_create(0);
66     struct icu_buf_utf16 *src16 = icu_buf_utf16_create(0);
67     struct icu_buf_utf16 *dest16 = icu_buf_utf16_create(0);
68
69
70     int src8cstr_len = strlen(src8cstr);
71     int chk8cstr_len = strlen(chk8cstr);
72
73     /* converting to UTF16 */
74     icu_utf16_from_utf8_cstr(src16, src8cstr, &status);
75
76     /* perform case mapping */
77     icu_utf16_casemap(dest16, src16, locale, action, &status);
78
79     /* converting to UTF8 */
80     icu_utf16_to_utf8(dest8, dest16, &status);
81
82     /* determine success */
83     if (dest8->utf8
84         && (dest8->utf8_len == strlen(chk8cstr))
85         && !strcmp(chk8cstr, (const char *) dest8->utf8))
86         success = 1;
87     else
88         success = 0;
89
90     /* report failures */
91     if (!success)
92     {
93         yaz_log(YLOG_WARN, "test_icu_casemap failed");
94         yaz_log(YLOG_LOG, "Original string:   '%s' (%d)",
95                 src8cstr, src8cstr_len);
96         yaz_log(YLOG_LOG, "icu_casemap '%s:%c' '%s' (%d)",
97                 locale, action, dest8->utf8, dest8->utf8_len);
98         yaz_log(YLOG_LOG, "expected string:   '%s' (%d)",
99                 chk8cstr, chk8cstr_len);
100     }
101
102     /* clean the buffers */
103     icu_buf_utf8_destroy(src8);
104     icu_buf_utf8_destroy(dest8);
105     icu_buf_utf16_destroy(src16);
106     icu_buf_utf16_destroy(dest16);
107
108     return success;
109 }
110
111 static void check_icu_casemap(void)
112 {
113     /* Locale 'en' */
114
115     /* successful tests */
116     YAZ_CHECK(test_icu_casemap("en", 'l',
117                                "A ReD fOx hunTS sQUirriLs",
118                                "a red fox hunts squirrils"));
119
120     YAZ_CHECK(test_icu_casemap("en", 'u',
121                                "A ReD fOx hunTS sQUirriLs",
122                                "A RED FOX HUNTS SQUIRRILS"));
123
124     YAZ_CHECK(test_icu_casemap("en", 'f',
125                                "A ReD fOx hunTS sQUirriLs",
126                                "a red fox hunts squirrils"));
127
128     YAZ_CHECK(test_icu_casemap("en", 't',
129                                "A ReD fOx hunTS sQUirriLs",
130                                "A Red Fox Hunts Squirrils"));
131
132     /* Locale 'da' */
133
134     /* success expected */
135     YAZ_CHECK(test_icu_casemap("da", 'l',
136                                "åh ÆbLE, øs fLØde i Åen efter bLåBærGRødeN",
137                                "åh æble, øs fløde i åen efter blåbærgrøden"));
138
139     YAZ_CHECK(test_icu_casemap("da", 'u',
140                                "åh ÆbLE, øs fLØde i Åen efter bLåBærGRødeN",
141                                "ÅH ÆBLE, ØS FLØDE I ÅEN EFTER BLÅBÆRGRØDEN"));
142
143     YAZ_CHECK(test_icu_casemap("da", 'f',
144                                "åh ÆbLE, øs fLØde i Åen efter bLåBærGRødeN",
145                                "åh æble, øs fløde i åen efter blåbærgrøden"));
146
147     YAZ_CHECK(test_icu_casemap("da", 't',
148                                "åh ÆbLE, øs fLØde i Åen efter bLåBærGRødeN",
149                                "Åh Æble, Øs Fløde I Åen Efter Blåbærgrøden"));
150
151     /* Locale 'de' */
152
153     /* success expected */
154     YAZ_CHECK(test_icu_casemap("de", 'l',
155                                "zWÖlf ärgerliche Würste rollen ÜBer die StRAße",
156                                "zwölf ärgerliche würste rollen über die straße"));
157
158     YAZ_CHECK(test_icu_casemap("de", 'u',
159                                "zWÖlf ärgerliche Würste rollen ÜBer die StRAße",
160                                "ZWÖLF ÄRGERLICHE WÜRSTE ROLLEN ÜBER DIE STRASSE"));
161
162     YAZ_CHECK(test_icu_casemap("de", 'f',
163                                "zWÖlf ärgerliche Würste rollen ÜBer die StRAße",
164                                "zwölf ärgerliche würste rollen über die strasse"));
165
166     YAZ_CHECK(test_icu_casemap("de", 't',
167                                "zWÖlf ärgerliche Würste rollen ÜBer die StRAße",
168                                "Zwölf Ärgerliche Würste Rollen Über Die Straße"));
169
170 }
171
172 static int test_icu_sortmap(const char *locale, int src_list_len,
173                             const char **src_list, const char **chk_list)
174 {
175     int success = 1;
176
177     UErrorCode status = U_ZERO_ERROR;
178
179     struct icu_buf_utf8 *buf8 = icu_buf_utf8_create(0);
180     struct icu_buf_utf16 *buf16 = icu_buf_utf16_create(0);
181
182     int i;
183
184     struct icu_termmap *list[src_list_len];
185
186     UCollator *coll = ucol_open(locale, &status);
187     icu_check_status(status);
188
189     if (U_FAILURE(status))
190         return 0;
191
192     /* assigning display terms and sort keys using buf 8 and buf16 */
193     for (i = 0; i < src_list_len; i++)
194     {
195
196         list[i] = (struct icu_termmap *) malloc(sizeof(struct icu_termmap));
197
198         /* copy display term */
199         strcpy(list[i]->disp_term, src_list[i]);
200
201         /* transforming to UTF16 */
202         icu_utf16_from_utf8_cstr(buf16, list[i]->disp_term, &status);
203         icu_check_status(status);
204
205         /* computing sortkeys */
206         icu_sortkey8_from_utf16(coll, buf8, buf16, &status);
207         icu_check_status(status);
208
209         /* assigning sortkeys */
210         memcpy(list[i]->sort_key, buf8->utf8, buf8->utf8_len);
211     }
212
213     /* do the sorting */
214     qsort(list, src_list_len, sizeof(struct icu_termmap *), icu_termmap_cmp);
215
216     /* checking correct sorting */
217     for (i = 0; i < src_list_len; i++)
218     {
219         if (0 != strcmp(list[i]->disp_term, chk_list[i])){
220             success = 0;
221         }
222     }
223
224     if (!success)
225     {
226         yaz_log(YLOG_LOG, "ERROR");
227         yaz_log(YLOG_LOG, "Input str:'%s':", locale);
228         for (i = 0; i < src_list_len; i++) {
229             yaz_log(YLOG_LOG, "  '%s'", list[i]->disp_term);
230         }
231         yaz_log(YLOG_LOG, "ICU sort: '%s':", locale);
232         for (i = 0; i < src_list_len; i++) {
233             yaz_log(YLOG_LOG, " '%s'", list[i]->disp_term);
234         }
235         yaz_log(YLOG_LOG, "Expected: '%s':", locale);
236         for (i = 0; i < src_list_len; i++) {
237             yaz_log(YLOG_LOG, " '%s'", chk_list[i]);
238         }
239     }
240
241     for (i = 0; i < src_list_len; i++)
242         free(list[i]);
243
244     ucol_close(coll);
245
246     icu_buf_utf8_destroy(buf8);
247     icu_buf_utf16_destroy(buf16);
248
249     return success;
250 }
251
252 static void check_icu_sortmap(void)
253 {
254     /* successful tests */
255     size_t en_1_len = 6;
256     const char *en_1_src[6] = {"z", "K", "a", "A", "Z", "k"};
257     const char *en_1_cck[6] = {"a", "A", "k", "K", "z", "Z"};
258     YAZ_CHECK(test_icu_sortmap("en", en_1_len, en_1_src, en_1_cck));
259     YAZ_CHECK(test_icu_sortmap("en_AU", en_1_len, en_1_src, en_1_cck));
260     YAZ_CHECK(test_icu_sortmap("en_CA", en_1_len, en_1_src, en_1_cck));
261     YAZ_CHECK(test_icu_sortmap("en_GB", en_1_len, en_1_src, en_1_cck));
262     YAZ_CHECK(test_icu_sortmap("en_US", en_1_len, en_1_src, en_1_cck));
263
264     /* successful tests */
265     {
266         size_t da_1_len = 6;
267         const char *da_1_src[6] = {"z", "å", "o", "æ", "a", "ø"};
268         const char *da_1_cck[6] = {"a", "o", "z", "æ", "ø", "å"};
269         YAZ_CHECK(test_icu_sortmap("da", da_1_len, da_1_src, da_1_cck));
270         YAZ_CHECK(test_icu_sortmap("da_DK", da_1_len, da_1_src, da_1_cck));
271     }
272     /* successful tests */
273     {
274         size_t de_1_len = 9;
275         const char *de_1_src[9] = {"u", "ä", "o", "t", "s", "ß", "ü", "ö", "a"};
276         const char *de_1_cck[9] = {"a","ä", "o", "ö", "s", "ß", "t", "u", "ü"};
277         YAZ_CHECK(test_icu_sortmap("de", de_1_len, de_1_src, de_1_cck));
278         YAZ_CHECK(test_icu_sortmap("de_AT", de_1_len, de_1_src, de_1_cck));
279         YAZ_CHECK(test_icu_sortmap("de_DE", de_1_len, de_1_src, de_1_cck));
280     }
281 }
282
283 static int test_icu_normalizer(const char *rules8cstr,
284                                const char *src8cstr,
285                                const char *chk8cstr)
286 {
287     int success = 0;
288
289     UErrorCode status = U_ZERO_ERROR;
290
291     struct icu_buf_utf16 *src16 = icu_buf_utf16_create(0);
292     struct icu_buf_utf16 *dest16 = icu_buf_utf16_create(0);
293     struct icu_buf_utf8 *dest8 = icu_buf_utf8_create(0);
294     struct icu_transform *transform
295         = icu_transform_create(rules8cstr, 'f', 0, &status);
296     icu_check_status(status);
297
298     icu_utf16_from_utf8_cstr(src16, src8cstr, &status);
299     icu_check_status(status);
300
301     icu_transform_trans(transform, dest16, src16, &status);
302     icu_check_status(status);
303
304     icu_utf16_to_utf8(dest8, dest16, &status);
305     icu_check_status(status);
306
307
308     if (!strcmp((const char *) dest8->utf8,
309                (const char *) chk8cstr))
310         success = 1;
311     else
312     {
313         success = 0;
314         yaz_log(YLOG_LOG, "Normalization");
315         yaz_log(YLOG_LOG, " Rules:      '%s'", rules8cstr);
316         yaz_log(YLOG_LOG, " Input:      '%s'", src8cstr);
317         yaz_log(YLOG_LOG, " Normalized: '%s'", dest8->utf8);
318         yaz_log(YLOG_LOG, " Expected:   '%s'", chk8cstr);
319     }
320
321     icu_transform_destroy(transform);
322     icu_buf_utf16_destroy(src16);
323     icu_buf_utf16_destroy(dest16);
324     icu_buf_utf8_destroy(dest8);
325
326     return success;
327 }
328
329 static void check_icu_normalizer(void)
330 {
331     YAZ_CHECK(test_icu_normalizer("[:Punctuation:] Any-Remove",
332                                   "Don't shoot!",
333                                   "Dont shoot"));
334
335     YAZ_CHECK(test_icu_normalizer("[:Control:] Any-Remove",
336                                   "Don't\n shoot!",
337                                   "Don't shoot!"));
338
339     YAZ_CHECK(test_icu_normalizer("[:Decimal_Number:] Any-Remove",
340                                   "This is 4 you!",
341                                   "This is  you!"));
342
343     YAZ_CHECK(test_icu_normalizer("Lower; [:^Letter:] Remove",
344                                   "Don't shoot!",
345                                   "dontshoot"));
346
347     YAZ_CHECK(test_icu_normalizer("[:^Number:] Remove",
348                                   "Monday 15th of April",
349                                   "15"));
350
351     YAZ_CHECK(test_icu_normalizer("Lower;"
352                                   "[[:WhiteSpace:][:Punctuation:]] Remove",
353                                   " word4you? ",
354                                   "word4you"));
355
356     YAZ_CHECK(test_icu_normalizer("NFD; [:Nonspacing Mark:] Remove; NFC",
357                                   "à côté de l'alcôve ovoïde",
358                                   "a cote de l'alcove ovoide"));
359 }
360
361 static int test_icu_tokenizer(const char *locale, char action,
362                               const char *src8cstr, int count)
363 {
364     int success = 1;
365
366     UErrorCode status = U_ZERO_ERROR;
367     struct icu_buf_utf16 *src16 = icu_buf_utf16_create(0);
368     struct icu_buf_utf16 *tkn16 = icu_buf_utf16_create(0);
369     struct icu_buf_utf8 *tkn8 = icu_buf_utf8_create(0);
370     struct icu_tokenizer *tokenizer = 0;
371
372     /* transforming to UTF16 */
373     icu_utf16_from_utf8_cstr(src16, src8cstr, &status);
374     icu_check_status(status);
375
376     /* set up tokenizer */
377     tokenizer = icu_tokenizer_create(locale, action, &status);
378     icu_check_status(status);
379     YAZ_CHECK(tokenizer);
380
381     /* attach text buffer to tokenizer */
382     icu_tokenizer_attach(tokenizer, src16, &status);
383     icu_check_status(status);
384
385     /* perform work on tokens */
386     while (icu_tokenizer_next_token(tokenizer, tkn16, &status))
387     {
388         icu_check_status(status);
389
390         /* converting to UTF8 */
391         icu_utf16_to_utf8(tkn8, tkn16, &status);
392     }
393
394     if (count != icu_tokenizer_token_count(tokenizer))
395     {
396         success = 0;
397         yaz_log(YLOG_LOG, "Tokenizer '%s:%c' Error:", locale, action);
398         yaz_log(YLOG_LOG, " Input:  '%s'", src8cstr);
399         yaz_log(YLOG_LOG, " Tokens: %d", icu_tokenizer_token_count(tokenizer));
400         yaz_log(YLOG_LOG, " Expected: %d", count);
401     }
402
403     icu_tokenizer_destroy(tokenizer);
404     icu_buf_utf16_destroy(src16);
405     icu_buf_utf16_destroy(tkn16);
406     icu_buf_utf8_destroy(tkn8);
407
408     return success;
409 }
410
411 static void check_icu_tokenizer(void)
412 {
413     const char *en_str
414         = "O Romeo, Romeo! wherefore art thou Romeo?";
415
416     YAZ_CHECK(test_icu_tokenizer("en", 's', en_str, 2));
417     YAZ_CHECK(test_icu_tokenizer("en", 'l', en_str, 7));
418     YAZ_CHECK(test_icu_tokenizer("en", 'w', en_str, 16));
419     YAZ_CHECK(test_icu_tokenizer("en", 'c', en_str, 41));
420
421     {
422         const char *da_str
423             = "Blåbærtærte. Denne kage stammer fra Finland. "
424             "Den er med blåbær, men alle sommerens forskellige bær kan bruges.";
425
426         YAZ_CHECK(test_icu_tokenizer("da", 's', da_str, 3));
427         YAZ_CHECK(test_icu_tokenizer("dar", 'l', da_str, 17));
428         YAZ_CHECK(test_icu_tokenizer("da", 'w', da_str, 37));
429         YAZ_CHECK(test_icu_tokenizer("da", 'c', da_str, 110));
430     }
431 }
432
433 static void check_icu_chain(void)
434 {
435     const char *en_str
436         = "O Romeo, Romeo! wherefore art thou\t Romeo?";
437
438     UErrorCode status = U_ZERO_ERROR;
439     struct icu_chain *chain = 0;
440
441     const char *xml_str = "<icu locale=\"en\">"
442         "<transform rule=\"[:Control:] Any-Remove\"/>"
443         "<tokenize rule=\"l\"/>"
444         "<transform rule=\"[[:WhiteSpace:][:Punctuation:]] Remove\"/>"
445         "<display/>"
446         "<casemap rule=\"l\"/>"
447         "</icu>";
448
449
450     xmlDoc *doc = xmlParseMemory(xml_str, strlen(xml_str));
451     xmlNode *xml_node = xmlDocGetRootElement(doc);
452     YAZ_CHECK(xml_node);
453
454     chain = icu_chain_xml_config(xml_node, 0, &status);
455
456     xmlFreeDoc(doc);
457     YAZ_CHECK(chain);
458     if (!chain)
459         return;
460
461     YAZ_CHECK(icu_chain_assign_cstr(chain, en_str, &status));
462
463     while (icu_chain_next_token(chain, &status))
464     {
465         yaz_log(YLOG_LOG, "%d '%s' '%s'",
466                 icu_chain_token_number(chain),
467                 icu_chain_token_norm(chain),
468                 icu_chain_token_display(chain));
469     }
470
471     YAZ_CHECK_EQ(icu_chain_token_number(chain), 7);
472
473
474     YAZ_CHECK(icu_chain_assign_cstr(chain, "what is this?", &status));
475
476     while (icu_chain_next_token(chain, &status))
477     {
478         yaz_log(YLOG_LOG, "%d '%s' '%s'",
479                 icu_chain_token_number(chain),
480                 icu_chain_token_norm(chain),
481                 icu_chain_token_display(chain));
482     }
483
484
485     YAZ_CHECK_EQ(icu_chain_token_number(chain), 3);
486
487     icu_chain_destroy(chain);
488 }
489
490
491 static void check_bug_1140(void)
492 {
493     UErrorCode status = U_ZERO_ERROR;
494     struct icu_chain *chain = 0;
495
496     const char *xml_str = "<icu locale=\"en\">"
497
498         /* if the first rule is normalize instead. Then it works */
499 #if 0
500         "<transform rule=\"[:Control:] Any-Remove\"/>"
501 #endif
502         "<tokenize rule=\"l\"/>"
503         "<transform rule=\"[[:WhiteSpace:][:Punctuation:]] Remove\"/>"
504         "<display/>"
505         "<casemap rule=\"l\"/>"
506         "</icu>";
507
508
509     xmlDoc *doc = xmlParseMemory(xml_str, strlen(xml_str));
510     xmlNode *xml_node = xmlDocGetRootElement(doc);
511     YAZ_CHECK(xml_node);
512
513     chain = icu_chain_xml_config(xml_node, 0, &status);
514
515     xmlFreeDoc(doc);
516     YAZ_CHECK(chain);
517     if (!chain)
518         return;
519
520     YAZ_CHECK(icu_chain_assign_cstr(
521                   chain,  "O Romeo, Romeo! wherefore art thou\t Romeo?",
522                   &status));
523
524     while (icu_chain_next_token(chain, &status))
525     {
526         ;
527         /* printf("%d '%s' '%s'\n",
528            icu_chain_token_number(chain),
529            icu_chain_token_norm(chain),
530            icu_chain_token_display(chain)); */
531     }
532
533
534     YAZ_CHECK_EQ(icu_chain_token_number(chain), 7);
535
536     YAZ_CHECK(icu_chain_assign_cstr(chain, "what is this?", &status));
537
538     while (icu_chain_next_token(chain, &status))
539     {
540         ;
541         /* printf("%d '%s' '%s'\n",
542            icu_chain_token_number(chain),
543            icu_chain_token_norm(chain),
544            icu_chain_token_display(chain)); */
545     }
546
547     /* we expect 'what' 'is' 'this', i.e. 3 tokens */
548     YAZ_CHECK_EQ(icu_chain_token_number(chain), 3);
549
550     icu_chain_destroy(chain);
551 }
552
553
554 static void check_chain_empty_token(void)
555 {
556     UErrorCode status = U_ZERO_ERROR;
557     struct icu_chain *chain = 0;
558
559     const char *xml_str = "<icu locale=\"en\">"
560         "<tokenize rule=\"w\"/>"
561         "<transform rule=\"[[:WhiteSpace:][:Punctuation:]] Remove\"/>"
562         "</icu>";
563
564     xmlDoc *doc = xmlParseMemory(xml_str, strlen(xml_str));
565     xmlNode *xml_node = xmlDocGetRootElement(doc);
566     YAZ_CHECK(xml_node);
567
568     chain = icu_chain_xml_config(xml_node, 0, &status);
569
570     xmlFreeDoc(doc);
571     YAZ_CHECK(chain);
572
573     YAZ_CHECK(icu_chain_assign_cstr(
574                   chain,  "a string with 15 tokenss and 8 displays",
575                   &status));
576
577     while (icu_chain_next_token(chain, &status))
578     {
579         ;
580         /* printf("%d '%s' '%s'\n",
581            icu_chain_token_number(chain),
582            icu_chain_token_norm(chain),
583            icu_chain_token_display(chain)); */
584     }
585
586     YAZ_CHECK_EQ(icu_chain_token_number(chain), 15);
587
588     icu_chain_destroy(chain);
589 }
590
591 static void check_chain_empty_chain(void)
592 {
593     UErrorCode status = U_ZERO_ERROR;
594     struct icu_chain *chain = 0;
595
596     const char *xml_str = "<icu locale=\"en\">"
597         "</icu>";
598
599     const char *src8 = "some 5487 weired !¤%&(/& sTuFf";
600     char *dest8 = 0;
601
602     xmlDoc *doc = xmlParseMemory(xml_str, strlen(xml_str));
603     xmlNode *xml_node = xmlDocGetRootElement(doc);
604     YAZ_CHECK(xml_node);
605
606     chain = icu_chain_xml_config(xml_node, 0, &status);
607
608     xmlFreeDoc(doc);
609     YAZ_CHECK(chain);
610
611     YAZ_CHECK(icu_chain_assign_cstr(
612                   chain,  src8,
613                   &status));
614
615     while (icu_chain_next_token(chain, &status))
616     {
617         ;
618         /* printf("%d '%s' '%s'\n",
619            icu_chain_token_number(chain),
620            icu_chain_token_norm(chain),
621            icu_chain_token_display(chain)); */
622     }
623
624     YAZ_CHECK_EQ(icu_chain_token_number(chain), 1);
625
626     dest8 = (char *) icu_chain_token_norm(chain);
627     YAZ_CHECK_EQ(strcmp(src8, dest8), 0);
628
629     icu_chain_destroy(chain);
630 }
631
632 static void check_icu_iter1(void)
633 {
634     UErrorCode status = U_ZERO_ERROR;
635     struct icu_chain *chain = 0;
636     xmlNode *xml_node;
637     yaz_icu_iter_t iter;
638
639     const char *xml_str = "<icu locale=\"en\">"
640         "<tokenize rule=\"w\"/>"
641         "<transform rule=\"[[:WhiteSpace:][:Punctuation:]] Remove\"/>"
642         "</icu>";
643
644     xmlDoc *doc = xmlParseMemory(xml_str, strlen(xml_str));
645     YAZ_CHECK(doc);
646     if (!doc)
647         return;
648     xml_node = xmlDocGetRootElement(doc);
649     YAZ_CHECK(xml_node);
650     if (!xml_node)
651         return ;
652
653     chain = icu_chain_xml_config(xml_node, 1, &status);
654
655     xmlFreeDoc(doc);
656     YAZ_CHECK(chain);
657
658     iter = icu_iter_create(chain);
659     icu_iter_first(iter, "a string with 15 tokens and 8 displays");
660     YAZ_CHECK(iter);
661     if (!iter)
662         return;
663     while (icu_iter_next(iter))
664     {
665         yaz_log(YLOG_LOG, "[%s]", icu_iter_get_norm(iter));
666     }
667     icu_iter_destroy(iter);
668     icu_chain_destroy(chain);
669 }
670
671 static int test_iter(struct icu_chain *chain, const char *input,
672                      const char *expected)
673 {
674     yaz_icu_iter_t iter = icu_iter_create(chain);
675     WRBUF result, second, sort_result;
676     int success = 1;
677
678     if (!iter)
679     {
680         yaz_log(YLOG_WARN, "test_iter: input=%s !iter", input);
681         return 0;
682     }
683
684     if (icu_iter_next(iter))
685     {
686         yaz_log(YLOG_WARN, "test_iter: expecting 0 before icu_iter_first");
687         return 0;
688     }
689
690     sort_result = wrbuf_alloc();
691     result = wrbuf_alloc();
692     icu_iter_first(iter, input);
693     while (icu_iter_next(iter))
694     {
695         const char *sort_str = icu_iter_get_sortkey(iter);
696         if (sort_str)
697         {
698             wrbuf_puts(sort_result, "[");
699             wrbuf_puts_escaped(sort_result, sort_str);
700             wrbuf_puts(sort_result, "]");
701         }
702         else
703         {
704             wrbuf_puts(sort_result, "[NULL]");
705         }
706         wrbuf_puts(result, "[");
707         wrbuf_puts(result, icu_iter_get_norm(iter));
708         wrbuf_puts(result, "]");
709     }
710     yaz_log(YLOG_LOG, "sortkey=%s", wrbuf_cstr(sort_result));
711     second = wrbuf_alloc();
712     icu_iter_first(iter, input);
713     while (icu_iter_next(iter))
714     {
715         wrbuf_puts(second, "[");
716         wrbuf_puts(second, icu_iter_get_norm(iter));
717         wrbuf_puts(second, "]");
718     }
719
720     icu_iter_destroy(iter);
721
722     if (strcmp(expected, wrbuf_cstr(result)))
723     {
724         yaz_log(YLOG_WARN, "test_iter: input=%s expected=%s got=%s",
725                 input, expected, wrbuf_cstr(result));
726         success = 0;
727     }
728
729     if (strcmp(expected, wrbuf_cstr(second)))
730     {
731         yaz_log(YLOG_WARN, "test_iter: input=%s expected=%s got=%s (2nd)",
732                 input, expected, wrbuf_cstr(second));
733         success = 0;
734     }
735
736     wrbuf_destroy(result);
737     wrbuf_destroy(second);
738     wrbuf_destroy(sort_result);
739     return success;
740 }
741
742 static void *iter_thread(void *p)
743 {
744     struct icu_chain *chain = (struct icu_chain *) p;
745     int i;
746
747     for (i = 0; i < 1000; i++)
748     {
749         YAZ_CHECK(test_iter(chain, "Adobe Acrobat Reader, 1991-1999.",
750                             "[adobe][acrobat][reader][1991][][1999][]"));
751     }
752     return 0;
753 }
754
755 static void check_iter_threads(struct icu_chain *chain)
756 {
757 #if YAZ_POSIX_THREADS
758 #define NO_THREADS 1
759
760     pthread_t t[NO_THREADS];
761     int i;
762
763     for (i = 0; i < NO_THREADS; i++)
764         pthread_create(t + i, 0, iter_thread, chain);
765
766     for (i = 0; i < NO_THREADS; i++)
767         pthread_join(t[i], 0);
768 #endif
769 }
770
771 static void check_icu_iter2(void)
772 {
773     UErrorCode status = U_ZERO_ERROR;
774     struct icu_chain *chain = 0;
775     xmlNode *xml_node;
776
777     const char *xml_str = "<icu locale=\"en\">"
778         "<transform rule=\"[:Control:] Any-Remove\"/>"
779         "<tokenize rule=\"l\"/>"
780         "<tokenize rule=\"w\"/>"
781         "<transform rule=\"[[:WhiteSpace:][:Punctuation:]] Remove\"/>"
782         "<display/>"
783         "<casemap rule=\"l\"/>"
784         "</icu>";
785
786     xmlDoc *doc = xmlParseMemory(xml_str, strlen(xml_str));
787     YAZ_CHECK(doc);
788     if (!doc)
789         return;
790     xml_node = xmlDocGetRootElement(doc);
791     YAZ_CHECK(xml_node);
792     if (!xml_node)
793         return ;
794
795     chain = icu_chain_xml_config(xml_node, 1, &status);
796
797     xmlFreeDoc(doc);
798     YAZ_CHECK(chain);
799     if (!chain)
800         return;
801
802     YAZ_CHECK(test_iter(chain, "Adobe Acrobat Reader, 1991-1999.",
803                         "[adobe][acrobat][reader][1991][][1999][]"));
804
805     YAZ_CHECK(test_iter(chain, "Νόταρης, Γιάννης Σωτ",
806                         "[νόταρης][γιάννης][σωτ]"));
807
808     check_iter_threads(chain);
809
810     icu_chain_destroy(chain);
811 }
812
813 static void check_icu_iter3(void)
814 {
815     UErrorCode status = U_ZERO_ERROR;
816     struct icu_chain *chain = 0;
817     xmlNode *xml_node;
818
819     const char *xml_str =
820         "<icu_chain id=\"sort\" locale=\"el\">\n"
821         "<transform rule=\"[:Control:] Any-Remove\"/>\n"
822         "<transform rule=\"[[:Control:][:WhiteSpace:][:Punctuation:]] Remove\"/>\n"
823         "<transform rule=\"NFD; [:Nonspacing Mark:] Remove; NFC\"/>\n"
824         "<casemap rule=\"l\"/>\n"
825         "<display/>\n"
826         "</icu_chain>\n";
827
828     xmlDoc *doc = xmlParseMemory(xml_str, strlen(xml_str));
829     YAZ_CHECK(doc);
830     if (!doc)
831         return;
832     xml_node = xmlDocGetRootElement(doc);
833     YAZ_CHECK(xml_node);
834     if (!xml_node)
835         return ;
836
837     chain = icu_chain_xml_config(xml_node, 1, &status);
838
839     xmlFreeDoc(doc);
840     YAZ_CHECK(chain);
841     if (!chain)
842         return;
843
844     YAZ_CHECK(test_iter(chain, "Adobe Acrobat Reader, 1991-1999.",
845                         "[adobeacrobatreader19911999]"));
846
847     YAZ_CHECK(test_iter(chain, "Νόταρης, Γιάννης Σωτ",
848                         "[νοταρηςγιαννηςσωτ]"));
849
850     icu_chain_destroy(chain);
851 }
852
853
854 static void check_icu_iter4(void)
855 {
856     UErrorCode status = U_ZERO_ERROR;
857     struct icu_chain *chain = 0;
858     xmlNode *xml_node;
859
860     const char *xml_str = "<icu locale=\"en\">"
861         "<transform rule=\"[:Control:] Any-Remove\"/>"
862         "<tokenize rule=\"l\"/>"
863         "<tokenize rule=\"w\"/>"
864         "<transform rule=\"[[:WhiteSpace:][:Punctuation:]] Remove\"/>"
865         "<display/>"
866         "<casemap rule=\"l\"/>"
867         "<join rule=\"\"/>"
868         "</icu>";
869
870     xmlDoc *doc = xmlParseMemory(xml_str, strlen(xml_str));
871     YAZ_CHECK(doc);
872     if (!doc)
873         return;
874     xml_node = xmlDocGetRootElement(doc);
875     YAZ_CHECK(xml_node);
876     if (!xml_node)
877         return ;
878
879     chain = icu_chain_xml_config(xml_node, 1, &status);
880
881     xmlFreeDoc(doc);
882     YAZ_CHECK(chain);
883     if (!chain)
884         return;
885
886     YAZ_CHECK(test_iter(chain, "Adobe Acrobat Reader, 1991-1999.",
887                         "[adobeacrobatreader19911999]"));
888
889     YAZ_CHECK(test_iter(chain, "Νόταρης, Γιάννης Σωτ",
890                         "[νόταρηςγιάννηςσωτ]"));
891
892     // check_iter_threads(chain);
893
894     icu_chain_destroy(chain);
895 }
896
897
898 #endif /* YAZ_HAVE_ICU */
899
900 int main(int argc, char **argv)
901 {
902     YAZ_CHECK_INIT(argc, argv);
903     YAZ_CHECK_LOG();
904
905 #if YAZ_HAVE_ICU
906
907     check_icu_casemap();
908     check_icu_sortmap();
909     check_icu_normalizer();
910     check_icu_tokenizer();
911     check_icu_chain();
912     check_chain_empty_token();
913     check_chain_empty_chain();
914     check_icu_iter1();
915     check_icu_iter2();
916     check_icu_iter3();
917     check_icu_iter4();
918
919     check_bug_1140();
920
921     u_cleanup();
922 #if YAZ_HAVE_XML2
923     xmlCleanupParser();
924 #endif
925
926 #else /* YAZ_HAVE_ICU */
927
928     yaz_log(YLOG_LOG, "ICU unit tests omitted");
929     YAZ_CHECK(0 == 0);
930
931 #endif /* YAZ_HAVE_ICU */
932
933     YAZ_CHECK_TERM;
934 }
935
936 /*
937  * Local variables:
938  * c-basic-offset: 4
939  * c-file-style: "Stroustrup"
940  * indent-tabs-mode: nil
941  * End:
942  * vim: shiftwidth=4 tabstop=8 expandtab
943  */
944