1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
6 * Copyright (C) 2009-2014, International Business Machines
7 * Corporation and others. All Rights Reserved.
9 *******************************************************************************
10 * file name: bidiconf.cpp
12 * tab size: 8 (not used)
15 * created on: 2009oct16
16 * created by: Markus W. Scherer
18 * BiDi conformance test, using the Unicode BidiTest.txt and BidiCharacterTest.txt files.
24 #include "unicode/utypes.h"
25 #include "unicode/ubidi.h"
26 #include "unicode/errorcode.h"
27 #include "unicode/localpointer.h"
28 #include "unicode/putil.h"
29 #include "unicode/unistr.h"
33 class BiDiConformanceTest
: public IntlTest
{
35 BiDiConformanceTest() :
36 directionBits(0), lineNumber(0), levelsCount(0), orderingCount(0),
39 void runIndexedTest(int32_t index
, UBool exec
, const char *&name
, char *par
=NULL
);
42 void TestBidiCharacterTest();
44 UBool
parseLevels(const char *&start
);
45 UBool
parseOrdering(const char *start
);
46 UBool
parseInputStringFromBiDiClasses(const char *&start
);
48 UBool
checkLevels(const UBiDiLevel actualLevels
[], int32_t actualCount
);
49 UBool
checkOrdering(UBiDi
*ubidi
);
51 void printErrorLine();
54 UBiDiLevel levels
[1000];
55 uint32_t directionBits
;
56 int32_t ordering
[1000];
59 int32_t orderingCount
;
61 UnicodeString inputString
;
62 const char *paraLevelName
;
63 char levelNameString
[12];
66 extern IntlTest
*createBiDiConformanceTest() {
67 return new BiDiConformanceTest();
70 void BiDiConformanceTest::runIndexedTest(int32_t index
, UBool exec
, const char *&name
, char * /*par*/) {
72 logln("TestSuite BiDiConformanceTest: ");
75 TESTCASE_AUTO(TestBidiTest
);
76 TESTCASE_AUTO(TestBidiCharacterTest
);
80 U_DEFINE_LOCAL_OPEN_POINTER(LocalStdioFilePointer
, FILE, fclose
);
82 UBool
BiDiConformanceTest::parseLevels(const char *&start
) {
85 while(*start
!=0 && *(start
=u_skipWhitespace(start
))!=0 && *start
!=';') {
87 levels
[levelsCount
++]=UBIDI_DEFAULT_LTR
;
91 uint32_t value
=(uint32_t)strtoul(start
, &end
, 10);
92 if(end
<=start
|| (!U_IS_INV_WHITESPACE(*end
) && *end
!=0 && *end
!=';')
93 || value
>(UBIDI_MAX_EXPLICIT_LEVEL
+1)) {
94 errln("\nError on line %d: Levels parse error at %s", (int)lineNumber
, start
);
98 levels
[levelsCount
++]=(UBiDiLevel
)value
;
99 directionBits
|=(1<<(value
&1));
106 UBool
BiDiConformanceTest::parseOrdering(const char *start
) {
108 while(*start
!=0 && *(start
=u_skipWhitespace(start
))!=0 && *start
!=';') {
110 uint32_t value
=(uint32_t)strtoul(start
, &end
, 10);
111 if(end
<=start
|| (!U_IS_INV_WHITESPACE(*end
) && *end
!=0 && *end
!=';') || value
>=1000) {
112 errln("\nError on line %d: Reorder parse error at %s", (int)lineNumber
, start
);
116 ordering
[orderingCount
++]=(int32_t)value
;
122 static const UChar charFromBiDiClass
[U_CHAR_DIRECTION_COUNT
]={
142 // new in Unicode 6.3/ICU 52
151 static UCharDirection U_CALLCONV
152 biDiConfUBiDiClassCallback(const void * /*context*/, UChar32 c
) {
153 for(int i
=0; i
<U_CHAR_DIRECTION_COUNT
; ++i
) {
154 if(c
==charFromBiDiClass
[i
]) {
155 return (UCharDirection
)i
;
158 // Character not in our hardcoded table.
159 // Should not occur during testing.
160 return U_BIDI_CLASS_DEFAULT
;
165 static const int8_t biDiClassNameLengths
[U_CHAR_DIRECTION_COUNT
+1]={
166 1, 1, 2, 2, 2, 2, 2, 1, 1, 2, 2, 3, 3, 2, 3, 3, 3, 3, 2, 3, 3, 3, 3, 0
169 UBool
BiDiConformanceTest::parseInputStringFromBiDiClasses(const char *&start
) {
170 inputString
.remove();
172 * Lengthy but fast BiDi class parser.
173 * A simple parser could terminate or extract the name string and use
174 * int32_t biDiClassInt=u_getPropertyValueEnum(UCHAR_BIDI_CLASS, bidiClassString);
175 * but that makes this test take significantly more time.
177 while(*start
!=0 && *(start
=u_skipWhitespace(start
))!=0 && *start
!=';') {
178 UCharDirection biDiClass
=U_CHAR_DIRECTION_COUNT
;
179 // Compare each character once until we have a match on
180 // a complete, short BiDi class name.
184 biDiClass
=U_LEFT_TO_RIGHT_EMBEDDING
;
185 } else if(start
[2]=='I') {
186 biDiClass
=U_LEFT_TO_RIGHT_ISOLATE
;
187 } else if(start
[2]=='O') {
188 biDiClass
=U_LEFT_TO_RIGHT_OVERRIDE
;
191 biDiClass
=U_LEFT_TO_RIGHT
;
193 } else if(start
[0]=='R') {
196 biDiClass
=U_RIGHT_TO_LEFT_EMBEDDING
;
197 } else if(start
[2]=='I') {
198 biDiClass
=U_RIGHT_TO_LEFT_ISOLATE
;
199 } else if(start
[2]=='O') {
200 biDiClass
=U_RIGHT_TO_LEFT_OVERRIDE
;
203 biDiClass
=U_RIGHT_TO_LEFT
;
205 } else if(start
[0]=='E') {
207 biDiClass
=U_EUROPEAN_NUMBER
;
208 } else if(start
[1]=='S') {
209 biDiClass
=U_EUROPEAN_NUMBER_SEPARATOR
;
210 } else if(start
[1]=='T') {
211 biDiClass
=U_EUROPEAN_NUMBER_TERMINATOR
;
213 } else if(start
[0]=='A') {
215 biDiClass
=U_RIGHT_TO_LEFT_ARABIC
;
216 } else if(start
[1]=='N') {
217 biDiClass
=U_ARABIC_NUMBER
;
219 } else if(start
[0]=='C' && start
[1]=='S') {
220 biDiClass
=U_COMMON_NUMBER_SEPARATOR
;
221 } else if(start
[0]=='B') {
223 biDiClass
=U_BOUNDARY_NEUTRAL
;
225 biDiClass
=U_BLOCK_SEPARATOR
;
227 } else if(start
[0]=='S') {
228 biDiClass
=U_SEGMENT_SEPARATOR
;
229 } else if(start
[0]=='W' && start
[1]=='S') {
230 biDiClass
=U_WHITE_SPACE_NEUTRAL
;
231 } else if(start
[0]=='O' && start
[1]=='N') {
232 biDiClass
=U_OTHER_NEUTRAL
;
233 } else if(start
[0]=='P' && start
[1]=='D') {
235 biDiClass
=U_POP_DIRECTIONAL_FORMAT
;
236 } else if(start
[2]=='I') {
237 biDiClass
=U_POP_DIRECTIONAL_ISOLATE
;
239 } else if(start
[0]=='N' && start
[1]=='S' && start
[2]=='M') {
240 biDiClass
=U_DIR_NON_SPACING_MARK
;
241 } else if(start
[0]=='F' && start
[1]=='S' && start
[2]=='I') {
242 biDiClass
=U_FIRST_STRONG_ISOLATE
;
244 // Now we verify that the class name is terminated properly,
245 // and not just the start of a longer word.
246 int8_t biDiClassNameLength
=biDiClassNameLengths
[biDiClass
];
247 char c
=start
[biDiClassNameLength
];
248 if(biDiClass
<U_CHAR_DIRECTION_COUNT
&& (U_IS_INV_WHITESPACE(c
) || c
==';' || c
==0)) {
249 inputString
.append(charFromBiDiClass
[biDiClass
]);
250 start
+=biDiClassNameLength
;
253 errln("\nError on line %d: BiDi class string not recognized at %s", (int)lineNumber
, start
);
260 void BiDiConformanceTest::TestBidiTest() {
261 IcuTestErrorCode
errorCode(*this, "TestBidiTest");
262 const char *sourceTestDataPath
=getSourceTestData(errorCode
);
263 if(errorCode
.errIfFailureAndReset("unable to find the source/test/testdata "
264 "folder (getSourceTestData())")) {
267 char bidiTestPath
[400];
268 strcpy(bidiTestPath
, sourceTestDataPath
);
269 strcat(bidiTestPath
, "BidiTest.txt");
270 LocalStdioFilePointer
bidiTestFile(fopen(bidiTestPath
, "r"));
271 if(bidiTestFile
.isNull()) {
272 errln("unable to open %s", bidiTestPath
);
275 LocalUBiDiPointer
ubidi(ubidi_open());
276 ubidi_setClassCallback(ubidi
.getAlias(), biDiConfUBiDiClassCallback
, NULL
,
277 NULL
, NULL
, errorCode
);
278 if(errorCode
.errIfFailureAndReset("ubidi_setClassCallback()")) {
285 // paraLevelName must be initialized in case the first non-comment line is in error
287 while(errorCount
<10 && fgets(line
, (int)sizeof(line
), bidiTestFile
.getAlias())!=NULL
) {
289 // Remove trailing comments and whitespace.
290 char *commentStart
=strchr(line
, '#');
291 if(commentStart
!=NULL
) {
295 const char *start
=u_skipWhitespace(line
);
297 continue; // Skip empty and comment-only lines.
301 if(0==strncmp(start
, "Levels:", 7)) {
303 if(!parseLevels(start
)) {
306 } else if(0==strncmp(start
, "Reorder:", 8)) {
307 if(!parseOrdering(start
+8)) {
311 // Skip unknown @Xyz: ...
313 if(!parseInputStringFromBiDiClasses(start
)) {
316 start
=u_skipWhitespace(start
);
318 errln("missing ; separator on input line %s", line
);
321 start
=u_skipWhitespace(start
+1);
323 uint32_t bitset
=(uint32_t)strtoul(start
, &end
, 16);
324 if(end
<=start
|| (!U_IS_INV_WHITESPACE(*end
) && *end
!=';' && *end
!=0)) {
325 errln("input bitset parse error at %s", start
);
328 // Loop over the bitset.
329 static const UBiDiLevel paraLevels
[]={ UBIDI_DEFAULT_LTR
, 0, 1, UBIDI_DEFAULT_RTL
};
330 static const char *const paraLevelNames
[]={ "auto/LTR", "LTR", "RTL", "auto/RTL" };
331 for(int i
=0; i
<=3; ++i
) {
333 ubidi_setPara(ubidi
.getAlias(), inputString
.getBuffer(), inputString
.length(),
334 paraLevels
[i
], NULL
, errorCode
);
335 const UBiDiLevel
*actualLevels
=ubidi_getLevels(ubidi
.getAlias(), errorCode
);
336 if(errorCode
.errIfFailureAndReset("ubidi_setPara() or ubidi_getLevels()")) {
337 errln("Input line %d: %s", (int)lineNumber
, line
);
340 paraLevelName
=paraLevelNames
[i
];
341 if(!checkLevels(actualLevels
, ubidi_getProcessedLength(ubidi
.getAlias()))) {
342 // continue outerLoop; does not exist in C++
343 // so just break out of the inner loop.
346 if(!checkOrdering(ubidi
.getAlias())) {
347 // continue outerLoop; does not exist in C++
348 // so just break out of the inner loop.
358 *******************************************************************************
360 * created on: 2013jul01
361 * created by: Matitiahu Allouche
363 This function performs a conformance test for implementations of the
364 Unicode Bidirectional Algorithm, specified in UAX #9: Unicode
365 Bidirectional Algorithm, at http://www.unicode.org/unicode/reports/tr9/
367 Each test case is represented in a single line which is read from a file
368 named BidiCharacter.txt. Empty, blank and comment lines may also appear
371 The format of the test data is specified below. Note that each test
372 case constitutes a single line of text; reordering is applied within a
373 single line and independently of a rendering engine, and rules L3 and L4
376 The number sign '#' is the comment character: everything is ignored from
377 the occurrence of '#' until the end of the line,
378 Empty lines and lines containing only spaces and/or comments are ignored.
380 Lines which represent test cases consist of 4 or 5 fields separated by a
381 semicolon. Each field consists of tokens separated by whitespace (space
382 or Tab). Whitespace before and after semicolons is optional.
384 Field 0: A sequence of hexadecimal code point values separated by space
386 Field 1: A value representing the paragraph direction, as follows:
387 - 0 represents left-to-right
388 - 1 represents right-to-left
389 - 2 represents auto-LTR according to rules P2 and P3 of the algorithm
390 - 3 represents auto-RTL according to rules P2 and P3 of the algorithm
391 - a negative number whose absolute value is taken as paragraph level;
392 this may be useful to test cases where the embedding level approaches
393 or exceeds the maximum embedding level.
395 Field 2: The resolved paragraph embedding level. If the input (field 0)
396 includes more than one paragraph, this field represents the
397 resolved level of the first paragraph.
399 Field 3: An ordered list of resulting levels for each token in field 0
400 (each token represents one source character).
401 The UBA does not assign levels to certain characters (e.g. LRO);
402 characters removed in rule X9 are indicated with an 'x'.
404 Field 4: An ordered list of indices showing the resulting visual ordering
405 from left to right; characters with a resolved level of 'x' are
406 skipped. The number are zero-based. Each index corresponds to
407 a character in the reordered (visual) string. It represents the
408 index of the source character in the input (field 0).
409 This field is optional. When it is absent, the visual ordering
414 # This is a comment line.
415 L L ON R ; 0 ; 0 ; 0 0 0 1 ; 0 1 2 3
416 L L ON R;0;0;0 0 0 1;0 1 2 3
418 # Note: in the next line, 'B' represents a block separator, not the letter 'B'.
419 LRE A B C PDF;2;0;x 2 0 0 x;1 2 3
420 # Note: in the next line, 'b' represents the letter 'b', not a block separator.
421 a b c 05d0 05d1 x ; 0 ; 0 ; 0 0 0 1 1 0 ; 0 1 2 4 3 5
423 a R R x ; 1 ; 1 ; 2 1 1 2
424 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
427 *******************************************************************************
429 enum { kMaxUtxt
= 32, kMaxUctl
= 16 };
431 void BiDiConformanceTest::TestBidiCharacterTest() {
432 IcuTestErrorCode
errorCode(*this, "TestBidiCharacterTest");
433 const char *sourceTestDataPath
=getSourceTestData(errorCode
);
434 if(errorCode
.errIfFailureAndReset("unable to find the source/test/testdata "
435 "folder (getSourceTestData())")) {
438 char bidiTestPath
[400];
439 strcpy(bidiTestPath
, sourceTestDataPath
);
440 strcat(bidiTestPath
, "BidiCharacterTest.txt");
441 LocalStdioFilePointer
bidiTestFile(fopen(bidiTestPath
, "r"));
442 if(bidiTestFile
.isNull()) {
443 errln("unable to open %s", bidiTestPath
);
446 LocalUBiDiPointer
ubidi(ubidi_open());
451 while(errorCount
<20 && fgets(line
, (int)sizeof(line
), bidiTestFile
.getAlias())!=NULL
) {
455 // Remove trailing comments and whitespace.
456 char *commentStart
=strchr(line
, '#');
457 if(commentStart
!=NULL
) {
461 const char *start
=u_skipWhitespace(line
);
463 continue; // Skip empty and comment-only lines.
465 // Parse the code point string in field 0.
466 UChar
*buffer
=inputString
.getBuffer(200);
467 int32_t length
=u_parseString(start
, buffer
, inputString
.getCapacity(), NULL
, errorCode
);
468 if(errorCode
.errIfFailureAndReset("Invalid string in field 0")) {
469 errln("Input line %d: %s", (int)lineNumber
, line
);
470 inputString
.remove();
473 inputString
.releaseBuffer(length
);
474 start
=strchr(start
, ';');
477 errln("\nError on line %d: Missing ; separator on line: %s", (int)lineNumber
, line
);
480 start
=u_skipWhitespace(start
+1);
482 int32_t paraDirection
=(int32_t)strtol(start
, &end
, 10);
483 UBiDiLevel paraLevel
=UBIDI_MAX_EXPLICIT_LEVEL
+2;
484 if(paraDirection
==0) {
488 else if(paraDirection
==1) {
492 else if(paraDirection
==2) {
493 paraLevel
=UBIDI_DEFAULT_LTR
;
494 paraLevelName
="Auto/LTR";
496 else if(paraDirection
==3) {
497 paraLevel
=UBIDI_DEFAULT_RTL
;
498 paraLevelName
="Auto/RTL";
500 else if(paraDirection
<0 && -paraDirection
<=(UBIDI_MAX_EXPLICIT_LEVEL
+1)) {
501 paraLevel
=(UBiDiLevel
)(-paraDirection
);
502 sprintf(levelNameString
, "%d", (int)paraLevel
);
503 paraLevelName
=levelNameString
;
505 if(end
<=start
|| (!U_IS_INV_WHITESPACE(*end
) && *end
!=';' && *end
!=0) ||
506 paraLevel
==(UBIDI_MAX_EXPLICIT_LEVEL
+2)) {
507 errln("\nError on line %d: Input paragraph direction incorrect at %s", (int)lineNumber
, start
);
511 start
=u_skipWhitespace(end
);
514 errln("\nError on line %d: Missing ; separator on line: %s", (int)lineNumber
, line
);
518 uint32_t resolvedParaLevel
=(uint32_t)strtoul(start
, &end
, 10);
519 if(end
<=start
|| (!U_IS_INV_WHITESPACE(*end
) && *end
!=';' && *end
!=0) ||
520 resolvedParaLevel
>1) {
521 errln("\nError on line %d: Resolved paragraph level incorrect at %s", (int)lineNumber
, start
);
525 start
=u_skipWhitespace(end
);
528 errln("\nError on line %d: Missing ; separator on line: %s", (int)lineNumber
, line
);
532 if(!parseLevels(start
)) {
535 start
=u_skipWhitespace(start
);
537 if(!parseOrdering(start
+1)) {
544 ubidi_setPara(ubidi
.getAlias(), inputString
.getBuffer(), inputString
.length(),
545 paraLevel
, NULL
, errorCode
);
546 const UBiDiLevel
*actualLevels
=ubidi_getLevels(ubidi
.getAlias(), errorCode
);
547 if(errorCode
.errIfFailureAndReset("ubidi_setPara() or ubidi_getLevels()")) {
548 errln("Input line %d: %s", (int)lineNumber
, line
);
551 UBiDiLevel actualLevel
;
552 if((actualLevel
=ubidi_getParaLevel(ubidi
.getAlias()))!=resolvedParaLevel
) {
554 errln("\nError on line %d: Wrong resolved paragraph level; expected %d actual %d",
555 (int)lineNumber
, resolvedParaLevel
, actualLevel
);
558 if(!checkLevels(actualLevels
, ubidi_getProcessedLength(ubidi
.getAlias()))) {
561 if(orderingCount
>=0 && !checkOrdering(ubidi
.getAlias())) {
565 // tests for ubidi_setParaWithControls
566 // skip 2 tests known not to work (out of 91678 cases, though
567 // only 86 of those tests use controls so 2.3% of those failing),
568 // still investigating these
569 if (lineNumber
>=212 && lineNumber
<=213) {
573 const UChar
* ubufPtr
= inputString
.getBuffer();
575 UChar utxt
[kMaxUtxt
];
576 UBiDiLevel ulev
[kMaxUtxt
];
577 int32_t offsets
[kMaxUctl
];
578 UChar
* uctlPtrs
[kMaxUctl
];
579 UChar uctl
[kMaxUctl
][5];
581 int32_t utxtLen
= 0, offsetsLen
= 0, ctlLen
= 0;
583 for (ubufIdx
= 0; ubufIdx
< inputString
.length(); ubufIdx
++) {
584 UChar uc
= ubufPtr
[ubufIdx
];
585 if ( (uc
>=0x202A && uc
<=0x202E) || (uc
>=0x2066 && uc
<=0x2069) ) {
586 // have a bidi control
591 // starting a new control sequence
592 if (offsetsLen
>= kMaxUctl
) {
595 offsets
[offsetsLen
] = utxtLen
;
596 uctlPtr
= &uctl
[offsetsLen
][0];
597 uctlPtrs
[offsetsLen
] = uctlPtr
;
600 uctlPtr
[ctlLen
++] = uc
;
603 if (utxtLen
>= kMaxUtxt
) {
608 levels
[utxtLen
] = levels
[ubufIdx
]; // will always have ubufIdx >= utxtLen so this is OK
612 levelsCount
= utxtLen
;
614 logln("Skipping BidiCharacterTest unsuitable for ubidi_setParaWithControls: %d: %s", (int)lineNumber
, line
);
615 continue; // can't use this test
617 if (offsetsLen
> 0 && offsets
[offsetsLen
-1] >= utxtLen
) {
619 ubidi_setContext(ubidi
.getAlias(), NULL
, 0, uctlPtrs
[offsetsLen
], -1, errorCode
);
621 ubidi_setContext(ubidi
.getAlias(), NULL
, 0, NULL
, 0, errorCode
);
623 ubidi_setParaWithControls(ubidi
.getAlias(), utxt
, utxtLen
, paraLevel
,
624 offsets
, offsetsLen
, NULL
, uctlPtrs
, errorCode
);
625 actualLevels
=ubidi_getLevels(ubidi
.getAlias(), errorCode
);
626 if(errorCode
.errIfFailureAndReset("ubidi_setContext()/ubidi_setParaWithControls()/ubidi_getLevels()")) {
627 errln("Input line %d: %s", (int)lineNumber
, line
);
630 if((actualLevel
=ubidi_getParaLevel(ubidi
.getAlias()))!=resolvedParaLevel
) {
632 errln("\nError on line %d: Wrong resolved paragraph level from ubidi_setParaWithControls; expected %d actual %d",
633 (int)lineNumber
, resolvedParaLevel
, actualLevel
);
636 if(!checkLevels(actualLevels
, ubidi_getProcessedLength(ubidi
.getAlias()))) {
642 static UChar
printLevel(UBiDiLevel level
) {
643 if(level
<UBIDI_DEFAULT_LTR
) {
650 static uint32_t getDirectionBits(const UBiDiLevel actualLevels
[], int32_t actualCount
) {
651 uint32_t actualDirectionBits
=0;
652 for(int32_t i
=0; i
<actualCount
; ++i
) {
653 actualDirectionBits
|=(1<<(actualLevels
[i
]&1));
655 return actualDirectionBits
;
658 UBool
BiDiConformanceTest::checkLevels(const UBiDiLevel actualLevels
[], int32_t actualCount
) {
660 if(levelsCount
!=actualCount
) {
661 errln("\nError on line %d: Wrong number of level values; expected %d actual %d",
662 (int)lineNumber
, (int)levelsCount
, (int)actualCount
);
665 for(int32_t i
=0; i
<actualCount
; ++i
) {
666 if(levels
[i
]!=actualLevels
[i
] && levels
[i
]<UBIDI_DEFAULT_LTR
) {
667 if(directionBits
!=3 && directionBits
==getDirectionBits(actualLevels
, actualCount
)) {
668 // ICU used a shortcut:
669 // Since the text is unidirectional, it did not store the resolved
670 // levels but just returns all levels as the paragraph level 0 or 1.
671 // The reordering result is the same, so this is fine.
674 errln("\nError on line %d: Wrong level value at index %d; expected %d actual %d",
675 (int)lineNumber
, (int)i
, levels
[i
], actualLevels
[i
]);
684 UnicodeString
els("Expected levels: ");
686 for(i
=0; i
<levelsCount
; ++i
) {
687 els
.append((UChar
)0x20).append(printLevel(levels
[i
]));
689 UnicodeString
als("Actual levels: ");
690 for(i
=0; i
<actualCount
; ++i
) {
691 als
.append((UChar
)0x20).append(printLevel(actualLevels
[i
]));
699 // Note: ubidi_setReorderingOptions(ubidi, UBIDI_OPTION_REMOVE_CONTROLS);
700 // does not work for custom BiDi class assignments
701 // and anyway also removes LRM/RLM/ZWJ/ZWNJ which is not desirable here.
702 // Therefore we just skip the indexes for BiDi controls while comparing
703 // with the expected ordering that has them omitted.
704 UBool
BiDiConformanceTest::checkOrdering(UBiDi
*ubidi
) {
706 IcuTestErrorCode
errorCode(*this, "checkOrdering()");
707 int32_t resultLength
=ubidi_getResultLength(ubidi
); // visual length including BiDi controls
708 int32_t i
, visualIndex
;
709 // Note: It should be faster to call ubidi_countRuns()/ubidi_getVisualRun()
710 // and loop over each run's indexes, but that seems unnecessary for this test code.
711 for(i
=visualIndex
=0; i
<resultLength
; ++i
) {
712 int32_t logicalIndex
=ubidi_getLogicalIndex(ubidi
, i
, errorCode
);
713 if(errorCode
.errIfFailureAndReset("ubidi_getLogicalIndex()")) {
714 errln("Input line %d: %s", (int)lineNumber
, line
);
717 if(levels
[logicalIndex
]>=UBIDI_DEFAULT_LTR
) {
718 continue; // BiDi control, omitted from expected ordering.
720 if(visualIndex
<orderingCount
&& logicalIndex
!=ordering
[visualIndex
]) {
721 errln("\nError on line %d: Wrong ordering value at visual index %d; expected %d actual %d",
722 (int)lineNumber
, (int)visualIndex
, ordering
[visualIndex
], logicalIndex
);
728 // visualIndex is now the visual length minus the BiDi controls,
729 // which should match the length of the BidiTest.txt ordering.
730 if(isOk
&& orderingCount
!=visualIndex
) {
731 errln("\nError on line %d: Wrong number of ordering values; expected %d actual %d",
732 (int)lineNumber
, (int)orderingCount
, (int)visualIndex
);
737 UnicodeString
eord("Expected ordering: ");
738 for(i
=0; i
<orderingCount
; ++i
) {
739 eord
.append((UChar
)0x20).append((UChar
)(0x30+ordering
[i
]));
741 UnicodeString
aord("Actual ordering: ");
742 for(i
=0; i
<resultLength
; ++i
) {
743 int32_t logicalIndex
=ubidi_getLogicalIndex(ubidi
, i
, errorCode
);
744 if(levels
[logicalIndex
]<UBIDI_DEFAULT_LTR
) {
745 aord
.append((UChar
)0x20).append((UChar
)(0x30+logicalIndex
));
754 void BiDiConformanceTest::printErrorLine() {
756 errln("Input line %5d: %s", (int)lineNumber
, line
);
757 errln(UnicodeString("Input string: ")+inputString
);
758 errln("Para level: %s", paraLevelName
);