• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
31 class BiDiConformanceTest : public IntlTest {
32 public:
BiDiConformanceTest()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();
41 private:
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 
createBiDiConformanceTest()64 extern IntlTest *createBiDiConformanceTest() {
65     return new BiDiConformanceTest();
66 }
67 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)68 void 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 
78 U_DEFINE_LOCAL_OPEN_POINTER(LocalStdioFilePointer, FILE, fclose);
79 
parseLevels(const char * & start)80 UBool 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 
parseOrdering(const char * start)104 UBool 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 
120 static 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 
147 U_CDECL_BEGIN
148 
149 static UCharDirection U_CALLCONV
biDiConfUBiDiClassCallback(const void *,UChar32 c)150 biDiConfUBiDiClassCallback(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 
161 U_CDECL_END
162 
163 static 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 
parseInputStringFromBiDiClasses(const char * & start)167 UBool 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 
TestBidiTest()258 void 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 
361 This function performs a conformance test for implementations of the
362 Unicode Bidirectional Algorithm, specified in UAX #9: Unicode
363 Bidirectional Algorithm, at http://www.unicode.org/unicode/reports/tr9/
364 
365 Each test case is represented in a single line which is read from a file
366 named BidiCharacter.txt.  Empty, blank and comment lines may also appear
367 in this file.
368 
369 The format of the test data is specified below.  Note that each test
370 case constitutes a single line of text; reordering is applied within a
371 single line and independently of a rendering engine, and rules L3 and L4
372 are out of scope.
373 
374 The number sign '#' is the comment character: everything is ignored from
375 the occurrence of '#' until the end of the line,
376 Empty lines and lines containing only spaces and/or comments are ignored.
377 
378 Lines which represent test cases consist of 4 or 5 fields separated by a
379 semicolon.  Each field consists of tokens separated by whitespace (space
380 or Tab).  Whitespace before and after semicolons is optional.
381 
382 Field 0: A sequence of hexadecimal code point values separated by space
383 
384 Field 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 
393 Field 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 
397 Field 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 
402 Field 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 
410 Examples:
411 
412 # This is a comment line.
413 L L ON R ; 0 ; 0 ; 0 0 0 1 ; 0 1 2 3
414 L 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'.
417 LRE 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.
419 a b c 05d0 05d1 x ; 0 ; 0 ; 0 0 0 1 1 0 ; 0 1 2 4 3 5
420 
421 a R R x ; 1 ; 1 ; 2 1 1 2
422 L 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 */
TestBidiCharacterTest()427 void 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 
printLevel(UBiDiLevel level)563 static UChar printLevel(UBiDiLevel level) {
564     if(level<UBIDI_DEFAULT_LTR) {
565         return 0x30+level;
566     } else {
567         return 0x78;  // 'x'
568     }
569 }
570 
getDirectionBits(const UBiDiLevel actualLevels[],int32_t actualCount)571 static 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 
checkLevels(const UBiDiLevel actualLevels[],int32_t actualCount)579 UBool 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.
checkOrdering(UBiDi * ubidi)625 UBool 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 
printErrorLine()675 void 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