1/*
2*******************************************************************************
3*
4*   Copyright (C) 2009-2014, International Business Machines
5*   Corporation and others.  All Rights Reserved.
6*
7*******************************************************************************
8*   file name:  bidiconf.cpp
9*   encoding:   US-ASCII
10*   tab size:   8 (not used)
11*   indentation:4
12*
13*   created on: 2009oct16
14*   created by: Markus W. Scherer
15*
16*   BiDi conformance test, using the Unicode BidiTest.txt and BidiCharacterTest.txt files.
17*/
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include "unicode/utypes.h"
23#include "unicode/ubidi.h"
24#include "unicode/errorcode.h"
25#include "unicode/localpointer.h"
26#include "unicode/putil.h"
27#include "unicode/unistr.h"
28#include "intltest.h"
29#include "uparse.h"
30
31class BiDiConformanceTest : public IntlTest {
32public:
33    BiDiConformanceTest() :
34        directionBits(0), lineNumber(0), levelsCount(0), orderingCount(0),
35        errorCount(0) {}
36
37    void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=NULL);
38
39    void TestBidiTest();
40    void TestBidiCharacterTest();
41private:
42    UBool parseLevels(const char *&start);
43    UBool parseOrdering(const char *start);
44    UBool parseInputStringFromBiDiClasses(const char *&start);
45
46    UBool checkLevels(const UBiDiLevel actualLevels[], int32_t actualCount);
47    UBool checkOrdering(UBiDi *ubidi);
48
49    void printErrorLine();
50
51    char line[10000];
52    UBiDiLevel levels[1000];
53    uint32_t directionBits;
54    int32_t ordering[1000];
55    int32_t lineNumber;
56    int32_t levelsCount;
57    int32_t orderingCount;
58    int32_t errorCount;
59    UnicodeString inputString;
60    const char *paraLevelName;
61    char levelNameString[12];
62};
63
64extern IntlTest *createBiDiConformanceTest() {
65    return new BiDiConformanceTest();
66}
67
68void BiDiConformanceTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char * /*par*/) {
69    if(exec) {
70        logln("TestSuite BiDiConformanceTest: ");
71    }
72    TESTCASE_AUTO_BEGIN;
73    TESTCASE_AUTO(TestBidiTest);
74    TESTCASE_AUTO(TestBidiCharacterTest);
75    TESTCASE_AUTO_END;
76}
77
78U_DEFINE_LOCAL_OPEN_POINTER(LocalStdioFilePointer, FILE, fclose);
79
80UBool BiDiConformanceTest::parseLevels(const char *&start) {
81    directionBits=0;
82    levelsCount=0;
83    while(*start!=0 && *(start=u_skipWhitespace(start))!=0 && *start!=';') {
84        if(*start=='x') {
85            levels[levelsCount++]=UBIDI_DEFAULT_LTR;
86            ++start;
87        } else {
88            char *end;
89            uint32_t value=(uint32_t)strtoul(start, &end, 10);
90            if(end<=start || (!U_IS_INV_WHITESPACE(*end) && *end!=0 && *end!=';')
91                          || value>(UBIDI_MAX_EXPLICIT_LEVEL+1)) {
92                errln("\nError on line %d: Levels parse error at %s", (int)lineNumber, start);
93                printErrorLine();
94                return FALSE;
95            }
96            levels[levelsCount++]=(UBiDiLevel)value;
97            directionBits|=(1<<(value&1));
98            start=end;
99        }
100    }
101    return TRUE;
102}
103
104UBool BiDiConformanceTest::parseOrdering(const char *start) {
105    orderingCount=0;
106    while(*start!=0 && *(start=u_skipWhitespace(start))!=0 && *start!=';') {
107        char *end;
108        uint32_t value=(uint32_t)strtoul(start, &end, 10);
109        if(end<=start || (!U_IS_INV_WHITESPACE(*end) && *end!=0 && *end!=';') || value>=1000) {
110            errln("\nError on line %d: Reorder parse error at %s", (int)lineNumber, start);
111            printErrorLine();
112            return FALSE;
113        }
114        ordering[orderingCount++]=(int32_t)value;
115        start=end;
116    }
117    return TRUE;
118}
119
120static const UChar charFromBiDiClass[U_CHAR_DIRECTION_COUNT]={
121    0x6c,   // 'l' for L
122    0x52,   // 'R' for R
123    0x33,   // '3' for EN
124    0x2d,   // '-' for ES
125    0x25,   // '%' for ET
126    0x39,   // '9' for AN
127    0x2c,   // ',' for CS
128    0x2f,   // '/' for B
129    0x5f,   // '_' for S
130    0x20,   // ' ' for WS
131    0x3d,   // '=' for ON
132    0x65,   // 'e' for LRE
133    0x6f,   // 'o' for LRO
134    0x41,   // 'A' for AL
135    0x45,   // 'E' for RLE
136    0x4f,   // 'O' for RLO
137    0x2a,   // '*' for PDF
138    0x60,   // '`' for NSM
139    0x7c,   // '|' for BN
140    // new in Unicode 6.3/ICU 52
141    0x53,   // 'S' for FSI
142    0x69,   // 'i' for LRI
143    0x49,   // 'I' for RLI
144    0x2e    // '.' for PDI
145};
146
147U_CDECL_BEGIN
148
149static UCharDirection U_CALLCONV
150biDiConfUBiDiClassCallback(const void * /*context*/, UChar32 c) {
151    for(int i=0; i<U_CHAR_DIRECTION_COUNT; ++i) {
152        if(c==charFromBiDiClass[i]) {
153            return (UCharDirection)i;
154        }
155    }
156    // Character not in our hardcoded table.
157    // Should not occur during testing.
158    return U_BIDI_CLASS_DEFAULT;
159}
160
161U_CDECL_END
162
163static const int8_t biDiClassNameLengths[U_CHAR_DIRECTION_COUNT+1]={
164    1, 1, 2, 2, 2, 2, 2, 1, 1, 2, 2, 3, 3, 2, 3, 3, 3, 3, 2, 3, 3, 3, 3, 0
165};
166
167UBool BiDiConformanceTest::parseInputStringFromBiDiClasses(const char *&start) {
168    inputString.remove();
169    /*
170     * Lengthy but fast BiDi class parser.
171     * A simple parser could terminate or extract the name string and use
172     *   int32_t biDiClassInt=u_getPropertyValueEnum(UCHAR_BIDI_CLASS, bidiClassString);
173     * but that makes this test take significantly more time.
174     */
175    while(*start!=0 && *(start=u_skipWhitespace(start))!=0 && *start!=';') {
176        UCharDirection biDiClass=U_CHAR_DIRECTION_COUNT;
177        // Compare each character once until we have a match on
178        // a complete, short BiDi class name.
179        if(start[0]=='L') {
180            if(start[1]=='R') {
181                if(start[2]=='E') {
182                    biDiClass=U_LEFT_TO_RIGHT_EMBEDDING;
183                } else if(start[2]=='I') {
184                    biDiClass=U_LEFT_TO_RIGHT_ISOLATE;
185                } else if(start[2]=='O') {
186                    biDiClass=U_LEFT_TO_RIGHT_OVERRIDE;
187                }
188            } else {
189                biDiClass=U_LEFT_TO_RIGHT;
190            }
191        } else if(start[0]=='R') {
192            if(start[1]=='L') {
193                if(start[2]=='E') {
194                    biDiClass=U_RIGHT_TO_LEFT_EMBEDDING;
195                } else if(start[2]=='I') {
196                    biDiClass=U_RIGHT_TO_LEFT_ISOLATE;
197                } else if(start[2]=='O') {
198                    biDiClass=U_RIGHT_TO_LEFT_OVERRIDE;
199                }
200            } else {
201                biDiClass=U_RIGHT_TO_LEFT;
202            }
203        } else if(start[0]=='E') {
204            if(start[1]=='N') {
205                biDiClass=U_EUROPEAN_NUMBER;
206            } else if(start[1]=='S') {
207                biDiClass=U_EUROPEAN_NUMBER_SEPARATOR;
208            } else if(start[1]=='T') {
209                biDiClass=U_EUROPEAN_NUMBER_TERMINATOR;
210            }
211        } else if(start[0]=='A') {
212            if(start[1]=='L') {
213                biDiClass=U_RIGHT_TO_LEFT_ARABIC;
214            } else if(start[1]=='N') {
215                biDiClass=U_ARABIC_NUMBER;
216            }
217        } else if(start[0]=='C' && start[1]=='S') {
218            biDiClass=U_COMMON_NUMBER_SEPARATOR;
219        } else if(start[0]=='B') {
220            if(start[1]=='N') {
221                biDiClass=U_BOUNDARY_NEUTRAL;
222            } else {
223                biDiClass=U_BLOCK_SEPARATOR;
224            }
225        } else if(start[0]=='S') {
226            biDiClass=U_SEGMENT_SEPARATOR;
227        } else if(start[0]=='W' && start[1]=='S') {
228            biDiClass=U_WHITE_SPACE_NEUTRAL;
229        } else if(start[0]=='O' && start[1]=='N') {
230            biDiClass=U_OTHER_NEUTRAL;
231        } else if(start[0]=='P' && start[1]=='D') {
232            if(start[2]=='F') {
233                biDiClass=U_POP_DIRECTIONAL_FORMAT;
234            } else if(start[2]=='I') {
235                biDiClass=U_POP_DIRECTIONAL_ISOLATE;
236            }
237        } else if(start[0]=='N' && start[1]=='S' && start[2]=='M') {
238            biDiClass=U_DIR_NON_SPACING_MARK;
239        } else if(start[0]=='F' && start[1]=='S' && start[2]=='I') {
240            biDiClass=U_FIRST_STRONG_ISOLATE;
241        }
242        // Now we verify that the class name is terminated properly,
243        // and not just the start of a longer word.
244        int8_t biDiClassNameLength=biDiClassNameLengths[biDiClass];
245        char c=start[biDiClassNameLength];
246        if(biDiClass<U_CHAR_DIRECTION_COUNT && (U_IS_INV_WHITESPACE(c) || c==';' || c==0)) {
247            inputString.append(charFromBiDiClass[biDiClass]);
248            start+=biDiClassNameLength;
249            continue;
250        }
251        errln("\nError on line %d: BiDi class string not recognized at %s", (int)lineNumber, start);
252        printErrorLine();
253        return FALSE;
254    }
255    return TRUE;
256}
257
258void BiDiConformanceTest::TestBidiTest() {
259    IcuTestErrorCode errorCode(*this, "TestBidiTest");
260    const char *sourceTestDataPath=getSourceTestData(errorCode);
261    if(errorCode.logIfFailureAndReset("unable to find the source/test/testdata "
262                                      "folder (getSourceTestData())")) {
263        return;
264    }
265    char bidiTestPath[400];
266    strcpy(bidiTestPath, sourceTestDataPath);
267    strcat(bidiTestPath, "BidiTest.txt");
268    LocalStdioFilePointer bidiTestFile(fopen(bidiTestPath, "r"));
269    if(bidiTestFile.isNull()) {
270        errln("unable to open %s", bidiTestPath);
271        return;
272    }
273    LocalUBiDiPointer ubidi(ubidi_open());
274    ubidi_setClassCallback(ubidi.getAlias(), biDiConfUBiDiClassCallback, NULL,
275                           NULL, NULL, errorCode);
276    if(errorCode.logIfFailureAndReset("ubidi_setClassCallback()")) {
277        return;
278    }
279    lineNumber=0;
280    levelsCount=0;
281    orderingCount=0;
282    errorCount=0;
283    // paraLevelName must be initialized in case the first non-comment line is in error
284    paraLevelName="N/A";
285    while(errorCount<10 && fgets(line, (int)sizeof(line), bidiTestFile.getAlias())!=NULL) {
286        ++lineNumber;
287        // Remove trailing comments and whitespace.
288        char *commentStart=strchr(line, '#');
289        if(commentStart!=NULL) {
290            *commentStart=0;
291        }
292        u_rtrim(line);
293        const char *start=u_skipWhitespace(line);
294        if(*start==0) {
295            continue;  // Skip empty and comment-only lines.
296        }
297        if(*start=='@') {
298            ++start;
299            if(0==strncmp(start, "Levels:", 7)) {
300                start+=7;
301                if(!parseLevels(start)) {
302                    return;
303                }
304            } else if(0==strncmp(start, "Reorder:", 8)) {
305                if(!parseOrdering(start+8)) {
306                    return;
307                }
308            }
309            // Skip unknown @Xyz: ...
310        } else {
311            if(!parseInputStringFromBiDiClasses(start)) {
312                return;
313            }
314            start=u_skipWhitespace(start);
315            if(*start!=';') {
316                errln("missing ; separator on input line %s", line);
317                return;
318            }
319            start=u_skipWhitespace(start+1);
320            char *end;
321            uint32_t bitset=(uint32_t)strtoul(start, &end, 16);
322            if(end<=start || (!U_IS_INV_WHITESPACE(*end) && *end!=';' && *end!=0)) {
323                errln("input bitset parse error at %s", start);
324                return;
325            }
326            // Loop over the bitset.
327            static const UBiDiLevel paraLevels[]={ UBIDI_DEFAULT_LTR, 0, 1, UBIDI_DEFAULT_RTL };
328            static const char *const paraLevelNames[]={ "auto/LTR", "LTR", "RTL", "auto/RTL" };
329            for(int i=0; i<=3; ++i) {
330                if(bitset&(1<<i)) {
331                    ubidi_setPara(ubidi.getAlias(), inputString.getBuffer(), inputString.length(),
332                                  paraLevels[i], NULL, errorCode);
333                    const UBiDiLevel *actualLevels=ubidi_getLevels(ubidi.getAlias(), errorCode);
334                    if(errorCode.logIfFailureAndReset("ubidi_setPara() or ubidi_getLevels()")) {
335                        errln("Input line %d: %s", (int)lineNumber, line);
336                        return;
337                    }
338                    paraLevelName=paraLevelNames[i];
339                    if(!checkLevels(actualLevels, ubidi_getProcessedLength(ubidi.getAlias()))) {
340                        // continue outerLoop;  does not exist in C++
341                        // so just break out of the inner loop.
342                        break;
343                    }
344                    if(!checkOrdering(ubidi.getAlias())) {
345                        // continue outerLoop;  does not exist in C++
346                        // so just break out of the inner loop.
347                        break;
348                    }
349                }
350            }
351        }
352    }
353}
354
355/*
356*******************************************************************************
357*
358*   created on: 2013jul01
359*   created by: Matitiahu Allouche
360
361This function performs a conformance test for implementations of the
362Unicode Bidirectional Algorithm, specified in UAX #9: Unicode
363Bidirectional Algorithm, at http://www.unicode.org/unicode/reports/tr9/
364
365Each test case is represented in a single line which is read from a file
366named BidiCharacter.txt.  Empty, blank and comment lines may also appear
367in this file.
368
369The format of the test data is specified below.  Note that each test
370case constitutes a single line of text; reordering is applied within a
371single line and independently of a rendering engine, and rules L3 and L4
372are out of scope.
373
374The number sign '#' is the comment character: everything is ignored from
375the occurrence of '#' until the end of the line,
376Empty lines and lines containing only spaces and/or comments are ignored.
377
378Lines which represent test cases consist of 4 or 5 fields separated by a
379semicolon.  Each field consists of tokens separated by whitespace (space
380or Tab).  Whitespace before and after semicolons is optional.
381
382Field 0: A sequence of hexadecimal code point values separated by space
383
384Field 1: A value representing the paragraph direction, as follows:
385    - 0 represents left-to-right
386    - 1 represents right-to-left
387    - 2 represents auto-LTR according to rules P2 and P3 of the algorithm
388    - 3 represents auto-RTL according to rules P2 and P3 of the algorithm
389    - a negative number whose absolute value is taken as paragraph level;
390      this may be useful to test cases where the embedding level approaches
391      or exceeds the maximum embedding level.
392
393Field 2: The resolved paragraph embedding level.  If the input (field 0)
394         includes more than one paragraph, this field represents the
395         resolved level of the first paragraph.
396
397Field 3: An ordered list of resulting levels for each token in field 0
398         (each token represents one source character).
399         The UBA does not assign levels to certain characters (e.g. LRO);
400         characters removed in rule X9 are indicated with an 'x'.
401
402Field 4: An ordered list of indices showing the resulting visual ordering
403         from left to right; characters with a resolved level of 'x' are
404         skipped.  The number are zero-based.  Each index corresponds to
405         a character in the reordered (visual) string. It represents the
406         index of the source character in the input (field 0).
407         This field is optional.  When it is absent, the visual ordering
408         is not verified.
409
410Examples:
411
412# This is a comment line.
413L L ON R ; 0 ; 0 ; 0 0 0 1 ; 0 1 2 3
414L L ON R;0;0;0 0 0 1;0 1 2 3
415
416# Note: in the next line, 'B' represents a block separator, not the letter 'B'.
417LRE A B C PDF;2;0;x 2 0 0 x;1 2 3
418# Note: in the next line, 'b' represents the letter 'b', not a block separator.
419a b c 05d0 05d1 x ; 0 ; 0 ; 0 0 0 1 1 0 ; 0 1 2 4 3 5
420
421a R R x ; 1 ; 1 ; 2 1 1 2
422L L R R R B R R L L L B ON ON ; 3 ; 0 ; 0 0 1 1 1 0 1 1 2 2 2 1 1 1
423
424*
425*******************************************************************************
426*/
427void BiDiConformanceTest::TestBidiCharacterTest() {
428    IcuTestErrorCode errorCode(*this, "TestBidiCharacterTest");
429    const char *sourceTestDataPath=getSourceTestData(errorCode);
430    if(errorCode.logIfFailureAndReset("unable to find the source/test/testdata "
431                                      "folder (getSourceTestData())")) {
432        return;
433    }
434    char bidiTestPath[400];
435    strcpy(bidiTestPath, sourceTestDataPath);
436    strcat(bidiTestPath, "BidiCharacterTest.txt");
437    LocalStdioFilePointer bidiTestFile(fopen(bidiTestPath, "r"));
438    if(bidiTestFile.isNull()) {
439        errln("unable to open %s", bidiTestPath);
440        return;
441    }
442    LocalUBiDiPointer ubidi(ubidi_open());
443    lineNumber=0;
444    levelsCount=0;
445    orderingCount=0;
446    errorCount=0;
447    while(errorCount<20 && fgets(line, (int)sizeof(line), bidiTestFile.getAlias())!=NULL) {
448        ++lineNumber;
449        paraLevelName="N/A";
450        inputString="N/A";
451        // Remove trailing comments and whitespace.
452        char *commentStart=strchr(line, '#');
453        if(commentStart!=NULL) {
454            *commentStart=0;
455        }
456        u_rtrim(line);
457        const char *start=u_skipWhitespace(line);
458        if(*start==0) {
459            continue;  // Skip empty and comment-only lines.
460        }
461        // Parse the code point string in field 0.
462        UChar *buffer=inputString.getBuffer(200);
463        int32_t length=u_parseString(start, buffer, inputString.getCapacity(), NULL, errorCode);
464        if(errorCode.logIfFailureAndReset("Invalid string in field 0")) {
465            errln("Input line %d: %s", (int)lineNumber, line);
466            inputString.remove();
467            continue;
468        }
469        inputString.releaseBuffer(length);
470        start=strchr(start, ';');
471        if(start==NULL) {
472            errorCount++;
473            errln("\nError on line %d: Missing ; separator on line: %s", (int)lineNumber, line);
474            continue;
475        }
476        start=u_skipWhitespace(start+1);
477        char *end;
478        int32_t paraDirection=(int32_t)strtol(start, &end, 10);
479        UBiDiLevel paraLevel=UBIDI_MAX_EXPLICIT_LEVEL+2;
480        if(paraDirection==0) {
481            paraLevel=0;
482            paraLevelName="LTR";
483        }
484        else if(paraDirection==1) {
485            paraLevel=1;
486            paraLevelName="RTL";
487        }
488        else if(paraDirection==2) {
489            paraLevel=UBIDI_DEFAULT_LTR;
490            paraLevelName="Auto/LTR";
491        }
492        else if(paraDirection==3) {
493            paraLevel=UBIDI_DEFAULT_RTL;
494            paraLevelName="Auto/RTL";
495        }
496        else if(paraDirection<0 && -paraDirection<=(UBIDI_MAX_EXPLICIT_LEVEL+1)) {
497            paraLevel=(UBiDiLevel)(-paraDirection);
498            sprintf(levelNameString, "%d", (int)paraLevel);
499            paraLevelName=levelNameString;
500        }
501        if(end<=start || (!U_IS_INV_WHITESPACE(*end) && *end!=';' && *end!=0) ||
502                         paraLevel==(UBIDI_MAX_EXPLICIT_LEVEL+2)) {
503            errln("\nError on line %d: Input paragraph direction incorrect at %s", (int)lineNumber, start);
504            printErrorLine();
505            continue;
506        }
507        start=u_skipWhitespace(end);
508        if(*start!=';') {
509            errorCount++;
510            errln("\nError on line %d: Missing ; separator on line: %s", (int)lineNumber, line);
511            continue;
512        }
513        start++;
514        uint32_t resolvedParaLevel=(uint32_t)strtoul(start, &end, 10);
515        if(end<=start || (!U_IS_INV_WHITESPACE(*end) && *end!=';' && *end!=0) ||
516           resolvedParaLevel>1) {
517            errln("\nError on line %d: Resolved paragraph level incorrect at %s", (int)lineNumber, start);
518            printErrorLine();
519            continue;
520        }
521        start=u_skipWhitespace(end);
522        if(*start!=';') {
523            errorCount++;
524            errln("\nError on line %d: Missing ; separator on line: %s", (int)lineNumber, line);
525            return;
526        }
527        start++;
528        if(!parseLevels(start)) {
529            continue;
530        }
531        start=u_skipWhitespace(start);
532        if(*start==';') {
533            if(!parseOrdering(start+1)) {
534                continue;
535            }
536        }
537        else
538            orderingCount=-1;
539
540        ubidi_setPara(ubidi.getAlias(), inputString.getBuffer(), inputString.length(),
541                      paraLevel, NULL, errorCode);
542        const UBiDiLevel *actualLevels=ubidi_getLevels(ubidi.getAlias(), errorCode);
543        if(errorCode.logIfFailureAndReset("ubidi_setPara() or ubidi_getLevels()")) {
544            errln("Input line %d: %s", (int)lineNumber, line);
545            continue;
546        }
547        UBiDiLevel actualLevel;
548        if((actualLevel=ubidi_getParaLevel(ubidi.getAlias()))!=resolvedParaLevel) {
549            printErrorLine();
550            errln("\nError on line %d: Wrong resolved paragraph level; expected %d actual %d",
551                   (int)lineNumber, resolvedParaLevel, actualLevel);
552            continue;
553        }
554        if(!checkLevels(actualLevels, ubidi_getProcessedLength(ubidi.getAlias()))) {
555            continue;
556        }
557        if(orderingCount>=0 && !checkOrdering(ubidi.getAlias())) {
558            continue;
559        }
560    }
561}
562
563static UChar printLevel(UBiDiLevel level) {
564    if(level<UBIDI_DEFAULT_LTR) {
565        return 0x30+level;
566    } else {
567        return 0x78;  // 'x'
568    }
569}
570
571static uint32_t getDirectionBits(const UBiDiLevel actualLevels[], int32_t actualCount) {
572    uint32_t actualDirectionBits=0;
573    for(int32_t i=0; i<actualCount; ++i) {
574        actualDirectionBits|=(1<<(actualLevels[i]&1));
575    }
576    return actualDirectionBits;
577}
578
579UBool BiDiConformanceTest::checkLevels(const UBiDiLevel actualLevels[], int32_t actualCount) {
580    UBool isOk=TRUE;
581    if(levelsCount!=actualCount) {
582        errln("\nError on line %d: Wrong number of level values; expected %d actual %d",
583              (int)lineNumber, (int)levelsCount, (int)actualCount);
584        isOk=FALSE;
585    } else {
586        for(int32_t i=0; i<actualCount; ++i) {
587            if(levels[i]!=actualLevels[i] && levels[i]<UBIDI_DEFAULT_LTR) {
588                if(directionBits!=3 && directionBits==getDirectionBits(actualLevels, actualCount)) {
589                    // ICU used a shortcut:
590                    // Since the text is unidirectional, it did not store the resolved
591                    // levels but just returns all levels as the paragraph level 0 or 1.
592                    // The reordering result is the same, so this is fine.
593                    break;
594                } else {
595                    errln("\nError on line %d: Wrong level value at index %d; expected %d actual %d",
596                          (int)lineNumber, (int)i, levels[i], actualLevels[i]);
597                    isOk=FALSE;
598                    break;
599                }
600            }
601        }
602    }
603    if(!isOk) {
604        printErrorLine();
605        UnicodeString els("Expected levels:   ");
606        int32_t i;
607        for(i=0; i<levelsCount; ++i) {
608            els.append((UChar)0x20).append(printLevel(levels[i]));
609        }
610        UnicodeString als("Actual   levels:   ");
611        for(i=0; i<actualCount; ++i) {
612            als.append((UChar)0x20).append(printLevel(actualLevels[i]));
613        }
614        errln(els);
615        errln(als);
616    }
617    return isOk;
618}
619
620// Note: ubidi_setReorderingOptions(ubidi, UBIDI_OPTION_REMOVE_CONTROLS);
621// does not work for custom BiDi class assignments
622// and anyway also removes LRM/RLM/ZWJ/ZWNJ which is not desirable here.
623// Therefore we just skip the indexes for BiDi controls while comparing
624// with the expected ordering that has them omitted.
625UBool BiDiConformanceTest::checkOrdering(UBiDi *ubidi) {
626    UBool isOk=TRUE;
627    IcuTestErrorCode errorCode(*this, "checkOrdering()");
628    int32_t resultLength=ubidi_getResultLength(ubidi);  // visual length including BiDi controls
629    int32_t i, visualIndex;
630    // Note: It should be faster to call ubidi_countRuns()/ubidi_getVisualRun()
631    // and loop over each run's indexes, but that seems unnecessary for this test code.
632    for(i=visualIndex=0; i<resultLength; ++i) {
633        int32_t logicalIndex=ubidi_getLogicalIndex(ubidi, i, errorCode);
634        if(errorCode.logIfFailureAndReset("ubidi_getLogicalIndex()")) {
635            errln("Input line %d: %s", (int)lineNumber, line);
636            return FALSE;
637        }
638        if(levels[logicalIndex]>=UBIDI_DEFAULT_LTR) {
639            continue;  // BiDi control, omitted from expected ordering.
640        }
641        if(visualIndex<orderingCount && logicalIndex!=ordering[visualIndex]) {
642            errln("\nError on line %d: Wrong ordering value at visual index %d; expected %d actual %d",
643                  (int)lineNumber, (int)visualIndex, ordering[visualIndex], logicalIndex);
644            isOk=FALSE;
645            break;
646        }
647        ++visualIndex;
648    }
649    // visualIndex is now the visual length minus the BiDi controls,
650    // which should match the length of the BidiTest.txt ordering.
651    if(isOk && orderingCount!=visualIndex) {
652        errln("\nError on line %d: Wrong number of ordering values; expected %d actual %d",
653              (int)lineNumber, (int)orderingCount, (int)visualIndex);
654        isOk=FALSE;
655    }
656    if(!isOk) {
657        printErrorLine();
658        UnicodeString eord("Expected ordering: ");
659        for(i=0; i<orderingCount; ++i) {
660            eord.append((UChar)0x20).append((UChar)(0x30+ordering[i]));
661        }
662        UnicodeString aord("Actual   ordering: ");
663        for(i=0; i<resultLength; ++i) {
664            int32_t logicalIndex=ubidi_getLogicalIndex(ubidi, i, errorCode);
665            if(levels[logicalIndex]<UBIDI_DEFAULT_LTR) {
666                aord.append((UChar)0x20).append((UChar)(0x30+logicalIndex));
667            }
668        }
669        errln(eord);
670        errln(aord);
671    }
672    return isOk;
673}
674
675void BiDiConformanceTest::printErrorLine() {
676    ++errorCount;
677    errln("Input line %5d:   %s", (int)lineNumber, line);
678    errln(UnicodeString("Input string:       ")+inputString);
679    errln("Para level:         %s", paraLevelName);
680}
681