Release 1.5.29.
[wine] / dlls / dwrite / tests / analyzer.c
1 /*
2  *    Text analyzing tests
3  *
4  * Copyright 2012 Nikolay Sivov for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define COBJMACROS
22
23 #include <assert.h>
24
25 #include "initguid.h"
26 #include "windows.h"
27 #include "dwrite.h"
28
29 #include "wine/test.h"
30
31 static IDWriteFactory *factory;
32
33 enum analysis_kind {
34     ScriptAnalysis,
35     LastKind
36 };
37
38 static const char *get_analysis_kind_name(enum analysis_kind kind)
39 {
40     switch (kind)
41     {
42     case ScriptAnalysis:
43         return "ScriptAnalysis";
44     default:
45         return "unknown";
46     }
47 }
48
49 struct script_analysis {
50     UINT32 pos;
51     UINT32 len;
52     DWRITE_SCRIPT_SHAPES shapes;
53 };
54
55 struct call_entry {
56     enum analysis_kind kind;
57     struct script_analysis sa;
58 };
59
60 struct testcontext {
61     enum analysis_kind kind;
62     int todo;
63     int *failcount;
64     const char *file;
65     int line;
66 };
67
68 struct call_sequence
69 {
70     int count;
71     int size;
72     struct call_entry *sequence;
73 };
74
75 #define NUM_CALL_SEQUENCES    1
76 #define ANALYZER_ID 0
77 static struct call_sequence *sequences[NUM_CALL_SEQUENCES];
78 static struct call_sequence *expected_seq[1];
79
80 static void add_call(struct call_sequence **seq, int sequence_index, const struct call_entry *call)
81 {
82     struct call_sequence *call_seq = seq[sequence_index];
83
84     if (!call_seq->sequence)
85     {
86         call_seq->size = 10;
87         call_seq->sequence = HeapAlloc(GetProcessHeap(), 0,
88                                       call_seq->size * sizeof (struct call_entry));
89     }
90
91     if (call_seq->count == call_seq->size)
92     {
93         call_seq->size *= 2;
94         call_seq->sequence = HeapReAlloc(GetProcessHeap(), 0,
95                                         call_seq->sequence,
96                                         call_seq->size * sizeof (struct call_entry));
97     }
98
99     assert(call_seq->sequence);
100
101     call_seq->sequence[call_seq->count++] = *call;
102 }
103
104 static inline void flush_sequence(struct call_sequence **seg, int sequence_index)
105 {
106     struct call_sequence *call_seq = seg[sequence_index];
107
108     HeapFree(GetProcessHeap(), 0, call_seq->sequence);
109     call_seq->sequence = NULL;
110     call_seq->count = call_seq->size = 0;
111 }
112
113 static inline void flush_sequences(struct call_sequence **seq, int n)
114 {
115     int i;
116     for (i = 0; i < n; i++)
117         flush_sequence(seq, i);
118 }
119
120 static void init_call_sequences(struct call_sequence **seq, int n)
121 {
122     int i;
123
124     for (i = 0; i < n; i++)
125         seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct call_sequence));
126 }
127
128 static void test_uint(UINT32 actual, UINT32 expected, const char *name, const struct testcontext *ctxt)
129 {
130     if (expected != actual && ctxt->todo)
131     {
132         (*ctxt->failcount)++;
133         ok_(ctxt->file, ctxt->line) (0, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name, expected, actual);
134     }
135     else
136         ok_(ctxt->file, ctxt->line) (expected == actual, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name,
137             expected, actual);
138 }
139
140 static void ok_sequence_(struct call_sequence **seq, int sequence_index,
141     const struct call_entry *expected, const char *context, int todo,
142     const char *file, int line)
143 {
144     struct call_sequence *call_seq = seq[sequence_index];
145     static const struct call_entry end_of_sequence = { LastKind };
146     const struct call_entry *actual, *sequence;
147     int failcount = 0;
148     struct testcontext ctxt;
149
150     add_call(seq, sequence_index, &end_of_sequence);
151
152     sequence = call_seq->sequence;
153     actual = sequence;
154
155     ctxt.failcount = &failcount;
156     ctxt.todo = todo;
157     ctxt.file = file;
158     ctxt.line = line;
159
160     while (expected->kind != LastKind && actual->kind != LastKind)
161     {
162         if (expected->kind == actual->kind)
163         {
164             ctxt.kind = expected->kind;
165
166             switch (actual->kind)
167             {
168             case ScriptAnalysis:
169             {
170                 const struct script_analysis *sa_act = &actual->sa;
171                 const struct script_analysis *sa_exp = &expected->sa;
172
173                 test_uint(sa_act->pos, sa_exp->pos, "position", &ctxt);
174                 test_uint(sa_act->len, sa_exp->len, "length", &ctxt);
175                 test_uint(sa_act->shapes, sa_exp->shapes, "shapes", &ctxt);
176
177                 break;
178             }
179             default:
180                 ok(0, "%s: callback not handled, %s\n", context, get_analysis_kind_name(actual->kind));
181             }
182             expected++;
183             actual++;
184         }
185         else if (todo)
186         {
187             failcount++;
188             todo_wine
189             {
190                 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
191                     context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
192             }
193
194             flush_sequence(seq, sequence_index);
195             return;
196         }
197         else
198         {
199             ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
200                 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
201             expected++;
202             actual++;
203         }
204     }
205
206     if (todo)
207     {
208         todo_wine
209         {
210             if (expected->kind != LastKind || actual->kind != LastKind)
211             {
212                 failcount++;
213                 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
214                     context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
215             }
216         }
217     }
218     else if (expected->kind != LastKind || actual->kind != LastKind)
219     {
220         ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
221             context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
222     }
223
224     if (todo && !failcount) /* succeeded yet marked todo */
225     {
226         todo_wine
227         {
228             ok_(file, line)(1, "%s: marked \"todo_wine\" but succeeds\n", context);
229         }
230     }
231
232     flush_sequence(seq, sequence_index);
233 }
234
235 #define ok_sequence(seq, index, exp, contx, todo) \
236         ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
237
238 static HRESULT WINAPI analysissink_QueryInterface(IDWriteTextAnalysisSink *iface, REFIID riid, void **obj)
239 {
240     if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown))
241     {
242         *obj = iface;
243         return S_OK;
244     }
245
246     *obj = NULL;
247     return E_NOINTERFACE;
248 }
249
250 static ULONG WINAPI analysissink_AddRef(IDWriteTextAnalysisSink *iface)
251 {
252     return 2;
253 }
254
255 static ULONG WINAPI analysissink_Release(IDWriteTextAnalysisSink *iface)
256 {
257     return 1;
258 }
259
260 static HRESULT WINAPI analysissink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface,
261     UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
262 {
263     struct call_entry entry;
264
265     entry.kind = ScriptAnalysis;
266     entry.sa.pos = position;
267     entry.sa.len = length;
268     entry.sa.shapes = sa->shapes;
269     add_call(sequences, ANALYZER_ID, &entry);
270     return S_OK;
271 }
272
273 static HRESULT WINAPI analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface,
274         UINT32 position,
275         UINT32 length,
276         DWRITE_LINE_BREAKPOINT const* breakpoints)
277 {
278     ok(0, "unexpected\n");
279     return E_NOTIMPL;
280 }
281
282 static HRESULT WINAPI analysissink_SetBidiLevel(IDWriteTextAnalysisSink *iface,
283         UINT32 position,
284         UINT32 length,
285         UINT8 explicitLevel,
286         UINT8 resolvedLevel)
287 {
288     ok(0, "unexpected\n");
289     return E_NOTIMPL;
290 }
291
292 static HRESULT WINAPI analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface,
293         UINT32 position,
294         UINT32 length,
295         IDWriteNumberSubstitution* substitution)
296 {
297     ok(0, "unexpected\n");
298     return E_NOTIMPL;
299 }
300
301 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl = {
302     analysissink_QueryInterface,
303     analysissink_AddRef,
304     analysissink_Release,
305     analysissink_SetScriptAnalysis,
306     analysissink_SetLineBreakpoints,
307     analysissink_SetBidiLevel,
308     analysissink_SetNumberSubstitution
309 };
310
311 static IDWriteTextAnalysisSink analysissink = { &analysissinkvtbl };
312
313 static HRESULT WINAPI analysissource_QueryInterface(IDWriteTextAnalysisSource *iface,
314     REFIID riid, void **obj)
315 {
316     ok(0, "QueryInterface not expected\n");
317     return E_NOTIMPL;
318 }
319
320 static ULONG WINAPI analysissource_AddRef(IDWriteTextAnalysisSource *iface)
321 {
322     ok(0, "AddRef not expected\n");
323     return 2;
324 }
325
326 static ULONG WINAPI analysissource_Release(IDWriteTextAnalysisSource *iface)
327 {
328     ok(0, "Release not expected\n");
329     return 1;
330 }
331
332 static const WCHAR *g_source;
333
334 static HRESULT WINAPI analysissource_GetTextAtPosition(IDWriteTextAnalysisSource *iface,
335     UINT32 position, WCHAR const** text, UINT32* text_len)
336 {
337     if (position >= lstrlenW(g_source))
338     {
339         *text = NULL;
340         *text_len = 0;
341     }
342     else
343     {
344         *text = &g_source[position];
345         *text_len = lstrlenW(g_source) - position;
346     }
347
348     return S_OK;
349 }
350
351 static HRESULT WINAPI analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource *iface,
352     UINT32 position, WCHAR const** text, UINT32* text_len)
353 {
354     ok(0, "unexpected\n");
355     return E_NOTIMPL;
356 }
357
358 static DWRITE_READING_DIRECTION WINAPI analysissource_GetParagraphReadingDirection(
359     IDWriteTextAnalysisSource *iface)
360 {
361     ok(0, "unexpected\n");
362     return DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
363 }
364
365 static HRESULT WINAPI analysissource_GetLocaleName(IDWriteTextAnalysisSource *iface,
366     UINT32 position, UINT32* text_len, WCHAR const** locale)
367 {
368     *locale = NULL;
369     *text_len = 0;
370     return S_OK;
371 }
372
373 static HRESULT WINAPI analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource *iface,
374     UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
375 {
376     ok(0, "unexpected\n");
377     return E_NOTIMPL;
378 }
379
380 static IDWriteTextAnalysisSourceVtbl analysissourcevtbl = {
381     analysissource_QueryInterface,
382     analysissource_AddRef,
383     analysissource_Release,
384     analysissource_GetTextAtPosition,
385     analysissource_GetTextBeforePosition,
386     analysissource_GetParagraphReadingDirection,
387     analysissource_GetLocaleName,
388     analysissource_GetNumberSubstitution
389 };
390
391 static IDWriteTextAnalysisSource analysissource = { &analysissourcevtbl };
392
393 struct sa_test {
394     const WCHAR string[50];
395     int item_count;
396     struct script_analysis sa[10];
397 };
398
399 static struct sa_test sa_tests[] = {
400     {
401       /* just 1 char string */
402       {'t',0}, 1,
403           { { 0, 1, DWRITE_SCRIPT_SHAPES_DEFAULT }}
404     },
405     {
406       {'t','e','s','t',0}, 1,
407           { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
408     },
409     {
410       {' ',' ',' ',' ','!','$','[','^','{','~',0}, 1,
411           { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
412     },
413     {
414       {' ',' ',' ','1','2',' ',0}, 1,
415           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
416     },
417     {
418       /* digits only */
419       {'1','2',0}, 1,
420           { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
421     },
422     {
423       /* Arabic */
424       {0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,0}, 1,
425           { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
426     },
427     {
428       /* Arabic */
429       {0x0627,0x0644,0x0635,0x0651,0x0650,0x062d,0x0629,0x064f,' ',0x062a,0x064e,
430        0x0627,0x062c,0x064c,' ',0x0639,0x064e,0x0644,0x0649,' ',
431        0x0631,0x064f,0x0624,0x0648,0x0633,0x0650,' ',0x0627,0x0644,
432        0x0623,0x0635,0x0650,0x062d,0x0651,0x064e,0x0627,0x0621,0x0650,0x06f0,0x06f5,0}, 1,
433           { { 0, 40, DWRITE_SCRIPT_SHAPES_DEFAULT }}
434     },
435     {
436       /* Arabic, Latin */
437       {'1','2','3','-','5','2',0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,'7','1','.',0}, 1,
438           { { 0, 16, DWRITE_SCRIPT_SHAPES_DEFAULT }}
439     },
440     {
441       /* Arabic, English */
442       {'A','B','C','-','D','E','F',' ',0x0621,0x0623,0x0624,0}, 2,
443           { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT },
444             { 8, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
445           }
446     },
447     {
448       /* leading space, Arabic, English */
449       {' ',0x0621,0x0623,0x0624,'A','B','C','-','D','E','F',0}, 2,
450           { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
451             { 4, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
452           }
453     },
454     {
455       /* English, Arabic, trailing space */
456       {'A','B','C','-','D','E','F',0x0621,0x0623,0x0624,' ',0}, 2,
457           { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
458             { 7, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
459           }
460     },
461     {
462       /* C1 Controls, Latin-1 Supplement */
463       {0x80,0x90,0x9f,0xa0,0xc0,0xb8,0xbf,0xc0,0xff,0}, 2,
464           { { 0, 3, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
465             { 3, 6, DWRITE_SCRIPT_SHAPES_DEFAULT },
466           }
467     },
468     {
469       /* Latin Extended-A */
470       {0x100,0x120,0x130,0x140,0x150,0x160,0x170,0x17f,0}, 1,
471           { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
472     },
473     {
474       /* Latin Extended-B */
475       {0x180,0x190,0x1bf,0x1c0,0x1c3,0x1c4,0x1cc,0x1dc,0x1ff,0x217,0x21b,0x24f,0}, 1,
476           { { 0, 12, DWRITE_SCRIPT_SHAPES_DEFAULT }}
477     },
478     {
479       /* IPA Extensions */
480       {0x250,0x260,0x270,0x290,0x2af,0}, 1,
481           { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
482     },
483     {
484       /* Spacing Modifier Letters */
485       {0x2b0,0x2ba,0x2d7,0x2dd,0x2ef,0x2ff,0}, 1,
486           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
487     },
488     {
489       /* Combining Diacritical Marks */
490       {0x300,0x320,0x340,0x345,0x350,0x36f,0}, 1,
491           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
492     },
493     {
494       /* Greek and Coptic */
495       {0x370,0x388,0x3d8,0x3e1,0x3e2,0x3fa,0x3ff,0}, 3,
496           { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
497             { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT },
498             { 5, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }
499           }
500     },
501     {
502       /* Cyrillic and Cyrillic Supplement */
503       {0x400,0x40f,0x410,0x44f,0x450,0x45f,0x460,0x481,0x48a,0x4f0,0x4fa,0x4ff,0x500,0x510,0x520,0}, 1,
504           { { 0, 15, DWRITE_SCRIPT_SHAPES_DEFAULT }}
505     },
506     {
507       /* Armenian */
508       {0x531,0x540,0x559,0x55f,0x570,0x589,0x58a,0}, 1,
509           { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
510     },
511     {
512       /* Hebrew */
513       {0x5e9,0x5dc,0x5d5,0x5dd,0}, 1,
514           { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
515     },
516     {
517       /* Latin, Hebrew, Latin */
518       {'p','a','r','t',' ','o','n','e',' ',0x5d7,0x5dc,0x5e7,' ',0x5e9,0x5ea,0x5d9,0x5d9,0x5dd,' ','p','a','r','t',' ','t','h','r','e','e',0}, 3,
519           { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT },
520             { 9, 10, DWRITE_SCRIPT_SHAPES_DEFAULT },
521             { 19, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
522     },
523     {
524       /* Syriac */
525       {0x710,0x712,0x712,0x714,'.',0}, 1,
526           { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
527     },
528     {
529       /* Arabic Supplement */
530       {0x750,0x760,0x76d,'.',0}, 1,
531           { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
532     },
533     {
534       /* Thaana */
535       {0x780,0x78e,0x798,0x7a6,0x7b0,'.',0}, 1,
536           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
537     },
538     {
539       /* N'Ko */
540       {0x7c0,0x7ca,0x7e8,0x7eb,0x7f6,'.',0}, 1,
541           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
542     },
543     {
544       /* Thaana */
545       {0x780,0x798,0x7a5,0x7a6,0x7b0,'.',0}, 1,
546           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
547     },
548     {
549       /* Devanagari */
550       {0x926,0x947,0x935,0x928,0x93e,0x917,0x930,0x940,'.',0}, 1,
551           { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
552     },
553     {
554       /* Bengali */
555       {0x9ac,0x9be,0x982,0x9b2,0x9be,'.',0}, 1,
556           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
557     },
558     {
559       /* Gurmukhi */
560       {0xa17,0xa41,0xa30,0xa2e,0xa41,0xa16,0xa40,'.',0}, 1,
561           { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
562     },
563     {
564       /* Gujarati */
565       {0xa97,0xac1,0xa9c,0xab0,0xabe,0xaa4,0xac0,'.',0}, 1,
566           { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
567     },
568     {
569       /* Oriya */
570       {0xb13,0xb21,0xb3c,0xb3f,0xb06,'.',0}, 1,
571           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
572     },
573     {
574       /* Tamil */
575       {0xba4,0xbae,0xbbf,0xbb4,0xbcd,'.',0}, 1,
576           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
577     },
578     {
579       /* Telugu */
580       {0xc24,0xc46,0xc32,0xc41,0xc17,0xc41,'.',0}, 1,
581           { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
582     },
583     {
584       /* Kannada */
585       {0xc95,0xca8,0xccd,0xca8,0xca1,'.',0}, 1,
586           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
587     },
588     {
589       /* Malayalam */
590       {0xd2e,0xd32,0xd2f,0xd3e,0xd33,0xd02,'.',0}, 1,
591           { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
592     },
593     {
594       /* Sinhala */
595       {0xd82,0xd85,0xd9a,0xdcf,'.',0}, 1,
596           { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
597     },
598     {
599       /* Thai */
600       {0x0e04,0x0e27,0x0e32,0x0e21,0x0e1e,0x0e22,0x0e32,0x0e22,0x0e32,0x0e21,
601        0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e44,0x0e2b,0x0e19,
602        0x0e04,0x0e27,0x0e32,0x0e21,0x0e2a, 0x0e33,0x0e40,0x0e23,0x0e47,0x0e08,
603        0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e19,0x0e31,0x0e48,0x0e19,'.',0}, 1,
604           { { 0, 42, DWRITE_SCRIPT_SHAPES_DEFAULT }}
605     },
606     {
607       /* Lao */
608       {0xead,0xeb1,0xe81,0xeaa,0xead,0xe99,0xea5,0xeb2,0xea7,'.',0}, 1,
609           { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
610     },
611     {
612       /* Tibetan */
613       {0xf04,0xf05,0xf0e,0x020,0xf51,0xf7c,0xf53,0xf0b,0xf5a,0xf53,0xf0b,
614        0xf51,0xf44,0xf0b,0xf54,0xf7c,0xf0d,'.',0}, 1,
615           { { 0, 18, DWRITE_SCRIPT_SHAPES_DEFAULT }}
616     },
617     {
618       /* Myanmar */
619       {0x1019,0x103c,0x1014,0x103a,0x1019,0x102c,0x1021,0x1000,0x1039,0x1001,0x101b,0x102c,'.',0}, 1,
620           { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
621     },
622     {
623       /* Georgian */
624       {0x10a0,0x10d0,0x10da,0x10f1,0x10fb,'.',0}, 1,
625           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
626     },
627     {
628       /* Hangul */
629       {0x1100,0x1110,0x1160,0x1170,0x11a8,'.',0}, 1,
630           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
631     },
632     {
633       /* Ethiopic */
634       {0x130d,0x12d5,0x12dd,0}, 1,
635           { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
636     },
637     {
638       /* Cherokee */
639       {0x13e3,0x13b3,0x13a9,0x0020,0x13a6,0x13ec,0x13c2,0x13af,0x13cd,0x13d7,0}, 1,
640           { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
641     },
642     {
643       /* Canadian */
644       {0x1403,0x14c4,0x1483,0x144e,0x1450,0x1466,0}, 1,
645           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
646     },
647     {
648       /* Ogham */
649       {0x169b,0x1691,0x168c,0x1690,0x168b,0x169c,0}, 1,
650           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
651     },
652     {
653       /* Runic */
654       {0x16a0,0x16a1,0x16a2,0x16a3,0x16a4,0x16a5,0}, 1,
655           { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
656     },
657     {
658       /* Khmer */
659       {0x1781,0x17c1,0x1798,0x179a,0x1797,0x17b6,0x179f,0x17b6,0x19e0,0}, 1,
660           { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
661     },
662     {
663       /* Mongolian */
664       {0x182e,0x1823,0x1829,0x182d,0x1823,0x182f,0x0020,0x182a,0x1822,0x1834,0x1822,0x182d,0x180c,0}, 1,
665           { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
666     },
667     {
668       /* Limbu */
669       {0x1900,0x1910,0x1920,0x1930,0}, 1,
670           { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
671     },
672     {
673       /* Tai Le */
674       {0x1956,0x196d,0x1970,0x1956,0x196c,0x1973,0x1951,0x1968,0x1952,0x1970,0}, 1,
675           { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
676     },
677     {
678       /* New Tai Lue */
679       {0x1992,0x19c4,0}, 1,
680           { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
681     },
682     {
683       /* Buginese */
684       {0x1a00,0x1a10,0}, 1,
685           { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
686     },
687     {
688       /* Tai Tham */
689       {0x1a20,0x1a40,0x1a50,0}, 1,
690           { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
691     },
692     {
693       /* Balinese */
694       {0x1b00,0x1b05,0x1b20,0}, 1,
695           { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
696     },
697     {
698       /* Sundanese */
699       {0x1b80,0x1b85,0x1ba0,0}, 1,
700           { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
701     },
702     {
703       /* Batak */
704       {0x1bc0,0x1be5,0x1bfc,0}, 1,
705           { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
706     },
707     {
708       /* Lepcha */
709       {0x1c00,0x1c20,0x1c40,0}, 1,
710           { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
711     },
712     {
713       /* Ol Chiki */
714       {0x1c50,0x1c5a,0x1c77,0}, 1,
715           { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
716     },
717     {
718       /* Sundanese Supplement */
719       {0x1cc0,0x1cc5,0x1cc8,0}, 1,
720           { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
721     },
722     {
723       /* Phonetic Extensions */
724       {0x1d00,0x1d40,0x1d70,0}, 1,
725           { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
726     },
727     /* keep this as end marker */
728     { {0} }
729 };
730
731 static void init_expected_sa(struct call_sequence **seq, const struct sa_test *test)
732 {
733     static const struct call_entry end_of_sequence = { LastKind };
734     int i;
735
736     flush_sequence(seq, 0);
737
738     /* add expected calls */
739     for (i = 0; i < test->item_count; i++)
740     {
741         struct call_entry call;
742
743         call.kind = ScriptAnalysis;
744         call.sa = test->sa[i];
745         add_call(seq, 0, &call);
746     }
747
748     /* and stop marker */
749     add_call(seq, 0, &end_of_sequence);
750 }
751
752 static void test_AnalyzeScript(void)
753 {
754     const struct sa_test *ptr = sa_tests;
755     IDWriteTextAnalyzer *analyzer;
756     HRESULT hr;
757
758     hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
759     ok(hr == S_OK, "got 0x%08x\n", hr);
760
761     while (*ptr->string)
762     {
763         g_source = ptr->string;
764
765         init_expected_sa(expected_seq, ptr);
766         hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource, 0, lstrlenW(g_source), &analysissink);
767         ok(hr == S_OK, "got 0x%08x\n", hr);
768         ok_sequence(sequences, ANALYZER_ID, expected_seq[0]->sequence, "AnalyzeScript", FALSE);
769         ptr++;
770     }
771
772     IDWriteTextAnalyzer_Release(analyzer);
773 }
774
775 START_TEST(analyzer)
776 {
777     HRESULT hr;
778
779     hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, &IID_IDWriteFactory, (IUnknown**)&factory);
780     ok(hr == S_OK, "got 0x%08x\n", hr);
781     if (hr != S_OK)
782     {
783         win_skip("failed to create factory\n");
784         return;
785     }
786
787     init_call_sequences(sequences, NUM_CALL_SEQUENCES);
788     init_call_sequences(expected_seq, 1);
789
790     test_AnalyzeScript();
791
792     IDWriteFactory_Release(factory);
793 }