winealsa: Fix capture overrun logging.
[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_ANALYSIS a;
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->a.script, sa_exp->a.script, "script", &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* scriptanalysis)
262 {
263     struct call_entry entry;
264
265     entry.kind = ScriptAnalysis;
266     entry.sa.pos = position;
267     entry.sa.len = length;
268     entry.sa.a = *scriptanalysis;
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[20];
395     int item_count;
396     struct script_analysis sa[10];
397 };
398
399 enum scriptcode {
400     Script_Arabic = 0,
401     Script_C1Controls = 12,
402     Script_Coptic = 13,
403     Script_Cyrillic = 16,
404     Script_Greek = 23,
405     Script_Latin  = 38,
406     Script_Symbol = 77
407 };
408
409 static struct sa_test sa_tests[] = {
410     {
411       /* just 1 char string */
412       {'t',0}, 1,
413           { { 0, 1, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
414     },
415     {
416       {'t','e','s','t',0}, 1,
417           { { 0, 4, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
418     },
419     {
420       {' ',' ',' ',' ','!','$','[','^','{','~',0}, 1,
421           { { 0, 10, { Script_Symbol, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
422     },
423     {
424       {' ',' ',' ','1','2',' ',0}, 1,
425           { { 0, 6, { Script_Symbol, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
426     },
427     {
428       /* digits only */
429       {'1','2',0}, 1,
430           { { 0, 2, { Script_Symbol, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
431     },
432     {
433       /* Arabic */
434       {0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,0}, 1,
435           { { 0, 7, { Script_Arabic, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
436     },
437     {
438       /* Arabic, Latin */
439       {'1','2','3','-','5','2',0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,'7','1','.',0}, 1,
440           { { 0, 16, { Script_Arabic, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
441     },
442     {
443       /* Arabic, English */
444       {'A','B','C','-','D','E','F',' ',0x0621,0x0623,0x0624,0}, 2,
445           { { 0, 8, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } },
446             { 8, 3, { Script_Arabic, DWRITE_SCRIPT_SHAPES_DEFAULT } },
447           }
448     },
449     {
450       /* leading space, Arabic, English */
451       {' ',0x0621,0x0623,0x0624,'A','B','C','-','D','E','F',0}, 2,
452           { { 0, 4, { Script_Arabic,  DWRITE_SCRIPT_SHAPES_DEFAULT } },
453             { 4, 7, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } },
454           }
455     },
456     {
457       /* English, Arabic, trailing space */
458       {'A','B','C','-','D','E','F',0x0621,0x0623,0x0624,' ',0}, 2,
459           { { 0, 7, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } },
460             { 7, 4, { Script_Arabic, DWRITE_SCRIPT_SHAPES_DEFAULT } },
461           }
462     },
463     {
464       /* C1 Controls, Latin-1 Supplement */
465       {0x80,0x90,0x9f,0xa0,0xc0,0xb8,0xbf,0xc0,0xff,0}, 2,
466           { { 0, 3, { Script_C1Controls, DWRITE_SCRIPT_SHAPES_DEFAULT } },
467             { 3, 6, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } },
468           }
469     },
470     {
471       /* Latin Extended-A */
472       {0x100,0x120,0x130,0x140,0x150,0x160,0x170,0x17f,0}, 1,
473           { { 0, 8, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
474     },
475     {
476       /* Latin Extended-B */
477       {0x180,0x190,0x1bf,0x1c0,0x1c3,0x1c4,0x1cc,0x1dc,0x1ff,0x217,0x21b,0x24f,0}, 1,
478           { { 0, 12, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
479     },
480     {
481       /* IPA Extensions */
482       {0x250,0x260,0x270,0x290,0x2af,0}, 1,
483           { { 0, 5, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
484     },
485     {
486       /* Spacing Modifier Letters */
487       {0x2b0,0x2ba,0x2d7,0x2dd,0x2ef,0x2ff,0}, 1,
488           { { 0, 6, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
489     },
490     {
491       /* Combining Diacritical Marks */
492       {0x300,0x320,0x340,0x345,0x350,0x36f,0}, 1,
493           { { 0, 6, { Script_Symbol, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
494     },
495     {
496       /* Greek and Coptic */
497       {0x370,0x388,0x3d8,0x3e1,0x3e2,0x3fa,0x3ff,0}, 3,
498           { { 0, 4, { Script_Greek, DWRITE_SCRIPT_SHAPES_DEFAULT } },
499             { 4, 1, { Script_Coptic, DWRITE_SCRIPT_SHAPES_DEFAULT } },
500             { 5, 2, { Script_Greek, DWRITE_SCRIPT_SHAPES_DEFAULT } }
501           }
502     },
503     {
504       /* Cyrillic and Cyrillic Supplement */
505       {0x400,0x40f,0x410,0x44f,0x450,0x45f,0x460,0x481,0x48a,0x4f0,0x4fa,0x4ff,0x500,0x510,0x520,0}, 1,
506           { { 0, 15, { Script_Cyrillic, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
507     },
508     /* keep this as end marker */
509     { {0} }
510 };
511
512 static void init_expected_sa(struct call_sequence **seq, const struct sa_test *test)
513 {
514     static const struct call_entry end_of_sequence = { LastKind };
515     int i;
516
517     flush_sequence(seq, 0);
518
519     /* add expected calls */
520     for (i = 0; i < test->item_count; i++)
521     {
522         struct call_entry call;
523
524         call.kind = ScriptAnalysis;
525         call.sa.pos = test->sa[i].pos;
526         call.sa.len = test->sa[i].len;
527         call.sa.a = test->sa[i].a;
528         add_call(seq, 0, &call);
529     }
530
531     /* and stop marker */
532     add_call(seq, 0, &end_of_sequence);
533 }
534
535 static void test_AnalyzeScript(void)
536 {
537     const struct sa_test *ptr = sa_tests;
538     IDWriteTextAnalyzer *analyzer;
539     HRESULT hr;
540
541     hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
542     ok(hr == S_OK, "got 0x%08x\n", hr);
543
544     while (*ptr->string)
545     {
546         g_source = ptr->string;
547
548         init_expected_sa(expected_seq, ptr);
549         hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource, 0, lstrlenW(g_source), &analysissink);
550         ok(hr == S_OK, "got 0x%08x\n", hr);
551         ok_sequence(sequences, ANALYZER_ID, expected_seq[0]->sequence, "AnalyzeScript", FALSE);
552         ptr++;
553     }
554
555     IDWriteTextAnalyzer_Release(analyzer);
556 }
557
558 START_TEST(analyzer)
559 {
560     HRESULT hr;
561
562     hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, &IID_IDWriteFactory, (IUnknown**)&factory);
563     ok(hr == S_OK, "got 0x%08x\n", hr);
564     if (hr != S_OK)
565     {
566         win_skip("failed to create factory\n");
567         return;
568     }
569
570     init_call_sequences(sequences, NUM_CALL_SEQUENCES);
571     init_call_sequences(expected_seq, 1);
572
573     test_AnalyzeScript();
574
575     IDWriteFactory_Release(factory);
576 }