4 * Copyright 2012 Nikolay Sivov for CodeWeavers
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.
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.
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
29 #include "wine/test.h"
31 static IDWriteFactory *factory;
38 static const char *get_analysis_kind_name(enum analysis_kind kind)
43 return "ScriptAnalysis";
49 struct script_analysis {
52 DWRITE_SCRIPT_ANALYSIS a;
56 enum analysis_kind kind;
57 struct script_analysis sa;
61 enum analysis_kind kind;
72 struct call_entry *sequence;
75 #define NUM_CALL_SEQUENCES 1
77 static struct call_sequence *sequences[NUM_CALL_SEQUENCES];
78 static struct call_sequence *expected_seq[1];
80 static void add_call(struct call_sequence **seq, int sequence_index, const struct call_entry *call)
82 struct call_sequence *call_seq = seq[sequence_index];
84 if (!call_seq->sequence)
87 call_seq->sequence = HeapAlloc(GetProcessHeap(), 0,
88 call_seq->size * sizeof (struct call_entry));
91 if (call_seq->count == call_seq->size)
94 call_seq->sequence = HeapReAlloc(GetProcessHeap(), 0,
96 call_seq->size * sizeof (struct call_entry));
99 assert(call_seq->sequence);
101 call_seq->sequence[call_seq->count++] = *call;
104 static inline void flush_sequence(struct call_sequence **seg, int sequence_index)
106 struct call_sequence *call_seq = seg[sequence_index];
108 HeapFree(GetProcessHeap(), 0, call_seq->sequence);
109 call_seq->sequence = NULL;
110 call_seq->count = call_seq->size = 0;
113 static inline void flush_sequences(struct call_sequence **seq, int n)
116 for (i = 0; i < n; i++)
117 flush_sequence(seq, i);
120 static void init_call_sequences(struct call_sequence **seq, int n)
124 for (i = 0; i < n; i++)
125 seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct call_sequence));
128 static void test_uint(UINT32 actual, UINT32 expected, const char *name, const struct testcontext *ctxt)
130 if (expected != actual && ctxt->todo)
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);
136 ok_(ctxt->file, ctxt->line) (expected == actual, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name,
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)
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;
148 struct testcontext ctxt;
150 add_call(seq, sequence_index, &end_of_sequence);
152 sequence = call_seq->sequence;
155 ctxt.failcount = &failcount;
160 while (expected->kind != LastKind && actual->kind != LastKind)
162 if (expected->kind == actual->kind)
164 ctxt.kind = expected->kind;
166 switch (actual->kind)
170 const struct script_analysis *sa_act = &actual->sa;
171 const struct script_analysis *sa_exp = &expected->sa;
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);
180 ok(0, "%s: callback not handled, %s\n", context, get_analysis_kind_name(actual->kind));
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));
194 flush_sequence(seq, sequence_index);
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));
210 if (expected->kind != LastKind || actual->kind != LastKind)
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));
218 else if (expected->kind != LastKind || actual->kind != LastKind)
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));
224 if (todo && !failcount) /* succeeded yet marked todo */
228 ok_(file, line)(1, "%s: marked \"todo_wine\" but succeeds\n", context);
232 flush_sequence(seq, sequence_index);
235 #define ok_sequence(seq, index, exp, contx, todo) \
236 ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
238 static HRESULT WINAPI analysissink_QueryInterface(IDWriteTextAnalysisSink *iface, REFIID riid, void **obj)
240 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown))
247 return E_NOINTERFACE;
250 static ULONG WINAPI analysissink_AddRef(IDWriteTextAnalysisSink *iface)
255 static ULONG WINAPI analysissink_Release(IDWriteTextAnalysisSink *iface)
260 static HRESULT WINAPI analysissink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface,
261 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* scriptanalysis)
263 struct call_entry entry;
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);
273 static HRESULT WINAPI analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface,
276 DWRITE_LINE_BREAKPOINT const* breakpoints)
278 ok(0, "unexpected\n");
282 static HRESULT WINAPI analysissink_SetBidiLevel(IDWriteTextAnalysisSink *iface,
288 ok(0, "unexpected\n");
292 static HRESULT WINAPI analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface,
295 IDWriteNumberSubstitution* substitution)
297 ok(0, "unexpected\n");
301 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl = {
302 analysissink_QueryInterface,
304 analysissink_Release,
305 analysissink_SetScriptAnalysis,
306 analysissink_SetLineBreakpoints,
307 analysissink_SetBidiLevel,
308 analysissink_SetNumberSubstitution
311 static IDWriteTextAnalysisSink analysissink = { &analysissinkvtbl };
313 static HRESULT WINAPI analysissource_QueryInterface(IDWriteTextAnalysisSource *iface,
314 REFIID riid, void **obj)
316 ok(0, "QueryInterface not expected\n");
320 static ULONG WINAPI analysissource_AddRef(IDWriteTextAnalysisSource *iface)
322 ok(0, "AddRef not expected\n");
326 static ULONG WINAPI analysissource_Release(IDWriteTextAnalysisSource *iface)
328 ok(0, "Release not expected\n");
332 static const WCHAR *g_source;
334 static HRESULT WINAPI analysissource_GetTextAtPosition(IDWriteTextAnalysisSource *iface,
335 UINT32 position, WCHAR const** text, UINT32* text_len)
337 if (position >= lstrlenW(g_source))
344 *text = &g_source[position];
345 *text_len = lstrlenW(g_source) - position;
351 static HRESULT WINAPI analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource *iface,
352 UINT32 position, WCHAR const** text, UINT32* text_len)
354 ok(0, "unexpected\n");
358 static DWRITE_READING_DIRECTION WINAPI analysissource_GetParagraphReadingDirection(
359 IDWriteTextAnalysisSource *iface)
361 ok(0, "unexpected\n");
362 return DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
365 static HRESULT WINAPI analysissource_GetLocaleName(IDWriteTextAnalysisSource *iface,
366 UINT32 position, UINT32* text_len, WCHAR const** locale)
373 static HRESULT WINAPI analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource *iface,
374 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
376 ok(0, "unexpected\n");
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
391 static IDWriteTextAnalysisSource analysissource = { &analysissourcevtbl };
394 const WCHAR string[20];
396 struct script_analysis sa[10];
401 Script_C1Controls = 12,
403 Script_Cyrillic = 16,
409 static struct sa_test sa_tests[] = {
411 /* just 1 char string */
413 { { 0, 1, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
416 {'t','e','s','t',0}, 1,
417 { { 0, 4, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
420 {' ',' ',' ',' ','!','$','[','^','{','~',0}, 1,
421 { { 0, 10, { Script_Symbol, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
424 {' ',' ',' ','1','2',' ',0}, 1,
425 { { 0, 6, { Script_Symbol, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
430 { { 0, 2, { Script_Symbol, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
434 {0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,0}, 1,
435 { { 0, 7, { Script_Arabic, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
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 } }}
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 } },
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 } },
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 } },
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 } },
471 /* Latin Extended-A */
472 {0x100,0x120,0x130,0x140,0x150,0x160,0x170,0x17f,0}, 1,
473 { { 0, 8, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
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 } }}
482 {0x250,0x260,0x270,0x290,0x2af,0}, 1,
483 { { 0, 5, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
486 /* Spacing Modifier Letters */
487 {0x2b0,0x2ba,0x2d7,0x2dd,0x2ef,0x2ff,0}, 1,
488 { { 0, 6, { Script_Latin, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
491 /* Combining Diacritical Marks */
492 {0x300,0x320,0x340,0x345,0x350,0x36f,0}, 1,
493 { { 0, 6, { Script_Symbol, DWRITE_SCRIPT_SHAPES_DEFAULT } }}
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 } }
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 } }}
508 /* keep this as end marker */
512 static void init_expected_sa(struct call_sequence **seq, const struct sa_test *test)
514 static const struct call_entry end_of_sequence = { LastKind };
517 flush_sequence(seq, 0);
519 /* add expected calls */
520 for (i = 0; i < test->item_count; i++)
522 struct call_entry call;
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);
531 /* and stop marker */
532 add_call(seq, 0, &end_of_sequence);
535 static void test_AnalyzeScript(void)
537 const struct sa_test *ptr = sa_tests;
538 IDWriteTextAnalyzer *analyzer;
541 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
542 ok(hr == S_OK, "got 0x%08x\n", hr);
546 g_source = ptr->string;
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);
555 IDWriteTextAnalyzer_Release(analyzer);
562 hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, &IID_IDWriteFactory, (IUnknown**)&factory);
563 ok(hr == S_OK, "got 0x%08x\n", hr);
566 win_skip("failed to create factory\n");
570 init_call_sequences(sequences, NUM_CALL_SEQUENCES);
571 init_call_sequences(expected_seq, 1);
573 test_AnalyzeScript();
575 IDWriteFactory_Release(factory);