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