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