2 *******************************************************************************
3 * Copyright (C) 2007-2013, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 *******************************************************************************
8 package com.ibm.icu.dev.test.bidi;
10 import java.util.Arrays;
12 import com.ibm.icu.text.Bidi;
13 import com.ibm.icu.text.BidiRun;
16 * Regression test for Bidi class override.
18 * @author Lina Kemmel, Matitiahu Allouche
21 public class TestBidi extends BidiTest {
23 private static final int MAXLEN = 256;
24 private static final String levelString = "............................";
26 public void testBidi() {
30 logln("\nEntering TestBidi");
31 bidi = new Bidi(MAXLEN, 0);
32 bidiLine = new Bidi();
34 doTests(bidi, bidiLine, false);
35 doTests(bidi, bidiLine, true);
37 logln("\nExiting TestBidi");
40 private void doTests(Bidi bidi, Bidi bidiLine, boolean countRunsFirst) {
45 int bidiTestCount = TestData.testCount();
47 for (testNumber = 0; testNumber < bidiTestCount; ++testNumber) {
48 TestData test = TestData.getTestData(testNumber);
49 string = getStringFromDirProps(test.dirProps);
50 paraLevel = test.paraLevel;
52 bidi.setPara(string, paraLevel, null);
53 logln("Bidi.setPara(tests[" + testNumber + "] OK, direction "
54 + bidi.getDirection() + " paraLevel "
56 } catch (Exception e) {
57 errln("Bidi.setPara(tests[" + testNumber + "] failed, direction "
58 + bidi.getDirection() + " paraLevel "
61 lineStart = test.lineStart;
62 if (lineStart == -1) {
63 doTest(bidi, testNumber, test, 0, countRunsFirst);
66 bidiLine = bidi.setLine(lineStart, test.lineLimit);
67 logln("Bidi.setLine(" + lineStart + ", " + test.lineLimit
68 + "), in tests[" + testNumber + "] OK, direction "
69 + bidiLine.getDirection() + " paraLevel "
70 + bidiLine.getBaseLevel());
71 doTest(bidiLine, testNumber, test, lineStart, countRunsFirst);
72 } catch (Exception e) {
73 errln("Bidi.setLine(" + lineStart + ", " + test.lineLimit
74 + "), in runAll test[" + testNumber + "] failed");
76 /* do it again using createLineBidi instead of setLine */
78 bidiLine = bidi.createLineBidi(lineStart, test.lineLimit);
79 logln("Bidi.createLineBidi(" + lineStart + ", " + test.lineLimit
80 + "), in tests[" + testNumber + "] OK, direction "
81 + bidiLine.getDirection() + " paraLevel "
82 + bidiLine.getBaseLevel());
83 doTest(bidiLine, testNumber, test, lineStart, countRunsFirst);
84 } catch (Exception e) {
85 errln("Bidi.createLineBidi(" + lineStart + ", " + test.lineLimit
86 + "), in runAll test[" + testNumber + "] failed");
92 private void doTest(Bidi bidi, int testNumber, TestData test,
93 int lineStart, boolean countRunsFirst) {
94 short[] dirProps = test.dirProps;
95 byte[] levels = test.levels;
96 int[] visualMap = test.visualMap;
97 int i, len = bidi.getLength(), logicalIndex = -1, runCount = 0;
100 if (countRunsFirst) {
101 logln("Calling Bidi.countRuns() first.");
103 runCount = bidi.countRuns();
104 } catch (IllegalStateException e) {
105 errln("Bidi.countRuns(test[" + testNumber + "]) failed");
108 logln("Calling Bidi.getLogicalMap() first.");
111 _testReordering(bidi, testNumber);
113 for (i = 0; i < len; ++i) {
114 logln(i + " " + bidi.getLevelAt(i) + " " + levelString
115 + TestData.dirPropNames[dirProps[lineStart + i]] + " "
116 + bidi.getVisualIndex(i));
119 log("\n-----levels:");
120 for (i = 0; i < len; ++i) {
124 log(" " + bidi.getLevelAt(i));
127 log("\n--reordered:");
128 for (i = 0; i < len; ++i) {
132 log(" " + bidi.getVisualIndex(i));
136 assertEquals("\nFailure in Bidi.getDirection(test[" + testNumber + "])",
137 test.direction, bidi.getDirection());
138 assertEquals("\nFailure in Bidi.getParaLevel(test[" + testNumber + "])",
139 test.resultLevel, bidi.getParaLevel());
141 for (i = 0; i < len; ++i) {
142 assertEquals("\nFailure in Bidi.getLevelAt(" + i +
143 ") in test[" + testNumber + "]",
144 levels[i], bidi.getLevelAt(i));
147 for (i = 0; i < len; ++i) {
149 logicalIndex = bidi.getVisualIndex(i);
150 } catch (Throwable th) {
151 errln("Bidi.getVisualIndex(" + i + ") in test[" + testNumber
154 if(visualMap[i] != logicalIndex) {
155 assertEquals("\nFailure in Bidi.getVisualIndex(" + i +
156 ") in test[" + testNumber + "])",
157 visualMap[i], logicalIndex);
161 if (!countRunsFirst) {
163 runCount = bidi.countRuns();
164 } catch (IllegalStateException e) {
165 errln("Bidi.countRuns(test[" + testNumber + "]) failed");
171 for (logicalIndex = 0; logicalIndex < len; ) {
172 level = bidi.getLevelAt(logicalIndex);
173 run = bidi.getLogicalRun(logicalIndex);
174 logicalIndex = run.getLimit();
175 level2 = run.getEmbeddingLevel();
176 assertEquals("Logical " + run.toString() +
177 " in test[" + testNumber + "]: wrong level",
179 if (--runCount < 0) {
180 errln("Bidi.getLogicalRun(test[" + testNumber
181 + "]): wrong number of runs compared to Bidi.countRuns() = "
186 errln("Bidi.getLogicalRun(test[" + testNumber
187 + "]): wrong number of runs compared to Bidi.countRuns() = "
194 private void _testReordering(Bidi bidi, int testNumber) {
201 int[] visualMap4 = new int[MAXLEN];
203 int i, length = bidi.getLength(),
204 destLength = bidi.getResultLength();
205 int runCount, visualIndex, logicalIndex = -1, logicalStart, runLength;
211 /* get the logical and visual maps from the object */
212 logicalMap1 = bidi.getLogicalMap();
213 if (logicalMap1 == null) {
214 errln("getLogicalMap in test " + testNumber + " is null");
215 logicalMap1 = new int[0];
218 visualMap1 = bidi.getVisualMap();
220 if (visualMap1 == null) {
221 errln("getVisualMap() in test " + testNumber + " is null");
222 visualMap1 = new int[0];
225 /* invert them both */
226 visualMap2 = Bidi.invertMap(logicalMap1);
227 logicalMap2 = Bidi.invertMap(visualMap1);
229 /* get them from the levels array, too */
230 levels = bidi.getLevels();
232 if (levels == null || levels.length != length) {
233 errln("getLevels() in test " + testNumber + " failed");
236 logicalMap3 = Bidi.reorderLogical(levels);
237 visualMap3 = Bidi.reorderVisual(levels);
239 /* get the visual map from the runs, too */
241 runCount = bidi.countRuns();
242 } catch (IllegalStateException e) {
243 errln("countRuns() in test " + testNumber + " failed");
247 logln("\n---- " + runCount + " runs");
250 for (i = 0; i < runCount; ++i) {
251 run = bidi.getVisualRun(i);
253 errln("null visual run encountered at index " + i +
254 ", in test " + testNumber);
257 odd = run.isOddRun();
258 logicalStart = run.getStart();
259 runLength = run.getLength();
260 log("(" + (run.isOddRun() ? "R" : "L"));
261 log(" @" + run.getStart() + '[' + run.getLength() + "])\n");
264 visualMap4[visualIndex++] = logicalStart++;
265 } while (--runLength > 0);
267 logicalStart += runLength; /* logicalLimit */
269 visualMap4[visualIndex++] = --logicalStart;
270 } while (--runLength > 0);
275 /* print all the maps */
276 logln("logical maps:");
277 for (i = 0; i < length; ++i) {
278 log(logicalMap1[i] + " ");
281 for (i = 0; i < length; ++i) {
282 log(logicalMap2[i] + " ");
285 for (i = 0; i < length; ++i) {
286 log(logicalMap3[i] + " ");
289 log("\nvisual maps:\n");
290 for (i = 0; i < destLength; ++i) {
291 log(visualMap1[i] + " ");
294 for (i = 0; i < destLength; ++i) {
295 log(visualMap2[i] + " ");
298 for (i = 0; i < length; ++i) {
299 log(visualMap3[i] + " ");
302 for (i = 0; i < length; ++i) {
303 log(visualMap4[i] + " ");
307 /* check that the indexes are the same between these and Bidi.getLogical/VisualIndex() */
308 for (i = 0; i < length; ++i) {
309 if (logicalMap1[i] != logicalMap2[i]) {
310 errln("Error in tests[" + testNumber + "]: (logicalMap1[" + i +
311 "] == " + logicalMap1[i] + ") != (logicalMap2[" + i +
312 "] == " + logicalMap2[i] + ")");
314 if (logicalMap1[i] != logicalMap3[i]) {
315 errln("Error in tests[" + testNumber + "]: (logicalMap1[" + i +
316 "] == " + logicalMap1[i] + ") != (logicalMap3[" + i +
317 "] == " + logicalMap3[i] + ")");
319 if (visualMap1[i] != visualMap2[i]) {
320 errln("Error in tests[" + testNumber + "]: (visualMap1[" + i +
321 "] == " + visualMap1[i] + ") != (visualMap2[" + i +
322 "] == " + visualMap2[i] + ")");
324 if (visualMap1[i] != visualMap3[i]) {
325 errln("Error in tests[" + testNumber + "]: (visualMap1[" + i +
326 "] == " + visualMap1[i] + ") != (visualMap3[" + i +
327 "] == " + visualMap3[i] + ")");
329 if (visualMap1[i] != visualMap4[i]) {
330 errln("Error in tests[" + testNumber + "]: (visualMap1[" + i +
331 "] == " + visualMap1[i] + ") != (visualMap4[" + i +
332 "] == " + visualMap4[i] + ")");
335 visualIndex = bidi.getVisualIndex(i);
336 } catch (Exception e) {
337 errln("Bidi.getVisualIndex(" + i + ") failed in tests[" +
340 if (logicalMap1[i] != visualIndex) {
341 errln("Error in tests[" + testNumber + "]: (logicalMap1[" + i +
342 "] == " + logicalMap1[i] + ") != (Bidi.getVisualIndex(" + i +
343 ") == " + visualIndex + ")");
346 logicalIndex = bidi.getLogicalIndex(i);
347 } catch (Exception e) {
348 errln("Bidi.getLogicalIndex(" + i + ") failed in tests[" +
351 if (visualMap1[i] != logicalIndex) {
352 errln("Error in tests[" + testNumber + "]: (visualMap1[" + i +
353 "] == " + visualMap1[i] + ") != (Bidi.getLogicalIndex(" + i +
354 ") == " + logicalIndex + ")");
359 private String getStringFromDirProps(short[] dirProps) {
362 if (dirProps == null) {
365 int length = dirProps.length;
366 char[] buffer = new char[length];
368 /* this part would have to be modified for UTF-x */
369 for (i = 0; i < length; ++i) {
370 buffer[i] = charFromDirProp[dirProps[i]];
372 return new String(buffer);
375 private void doMisc() {
376 /* Miscellaneous tests to exercize less popular code paths */
377 Bidi bidi = new Bidi(120, 66), bidiLine;
379 assertEquals("\nwriteReverse should return an empty string",
380 "", Bidi.writeReverse("", 0));
382 bidi.setPara("", Bidi.LTR, null);
383 assertEquals("\nwriteReordered should return an empty string",
384 "", bidi.writeReordered(0));
386 bidi.setPara("abc", Bidi.LTR, null);
387 assertEquals("\ngetRunStart should return 0",
388 0, bidi.getRunStart(0));
389 assertEquals("\ngetRunLimit should return 3",
390 3, bidi.getRunLimit(0));
392 bidi.setPara("abc ", Bidi.RTL, null);
393 bidiLine = bidi.setLine(0, 6);
394 for (int i = 3; i < 6; i++) {
395 assertEquals("\nTrailing space at " + i + " should get paragraph level",
396 Bidi.RTL, bidiLine.getLevelAt(i));
399 bidi.setPara("abc def", Bidi.RTL, null);
400 bidiLine = bidi.setLine(0, 6);
401 for (int i = 3; i < 6; i++) {
402 assertEquals("\nTrailing space at " + i + " should get paragraph level",
403 Bidi.RTL, bidiLine.getLevelAt(i));
406 bidi.setPara("abcdefghi ", Bidi.RTL, null);
407 bidiLine = bidi.setLine(0, 6);
408 for (int i = 3; i < 6; i++) {
409 assertEquals("\nTrailing char at " + i + " should get level 2",
410 2, bidiLine.getLevelAt(i));
413 bidi.setReorderingOptions(Bidi.OPTION_REMOVE_CONTROLS);
414 bidi.setPara("\u200eabc def", Bidi.RTL, null);
415 bidiLine = bidi.setLine(0, 6);
416 assertEquals("\nWrong result length", 5, bidiLine.getResultLength());
418 bidi.setPara("abcdefghi", Bidi.LTR, null);
419 bidiLine = bidi.setLine(0, 6);
420 assertEquals("\nWrong direction #1", Bidi.LTR, bidiLine.getDirection());
422 bidi.setPara("", Bidi.LTR, null);
423 byte[] levels = bidi.getLevels();
424 assertEquals("\nWrong number of level elements", 0, levels.length);
425 assertEquals("\nWrong number of runs #1", 0, bidi.countRuns());
427 bidi.setPara(" ", Bidi.RTL, null);
428 bidiLine = bidi.setLine(0, 6);
429 assertEquals("\nWrong number of runs #2", 1, bidiLine.countRuns());
431 bidi.setPara("a\u05d0 bc", Bidi.RTL, null);
432 bidiLine = bidi.setLine(0, 6);
433 assertEquals("\nWrong direction #2", Bidi.MIXED, bidi.getDirection());
434 assertEquals("\nWrong direction #3", Bidi.MIXED, bidiLine.getDirection());
435 assertEquals("\nWrong number of runs #3", 2, bidiLine.countRuns());
437 int[] map = Bidi.reorderLogical(null);
438 assertTrue("\nWe should have got a null map #1", map == null);
439 map = Bidi.reorderLogical(new byte[] {0,126, 127});
440 assertTrue("\nWe should have got a null map #2", map == null);
441 map = Bidi.reorderVisual(null);
442 assertTrue("\nWe should have got a null map #3", map == null);
443 map = Bidi.reorderVisual(new byte[] {0, -1, 4});
444 assertTrue("\nWe should have got a null map #4", map == null);
446 map = Bidi.invertMap(null);
447 assertTrue("\nWe should have got a null map #5", map == null);
448 map = Bidi.invertMap(new int[] {0,1,-1,5,4});
449 assertTrue("\nUnexpected inverted Map",
450 Arrays.equals(map, new int[] {0,1,-1,-1,4,3}));
452 bidi.setPara("", Bidi.LTR, null);
453 map = bidi.getLogicalMap();
454 assertTrue("\nMap should have length==0 #1", map.length == 0);
455 map = bidi.getVisualMap();
456 assertTrue("\nMap should have length==0 #2", map.length == 0);
458 /* test BidiRun.toString and allocation of run memory > 1 */
459 bidi.setPara("abc", Bidi.LTR, null);
460 assertEquals("\nWrong run display", "BidiRun 0 - 3 @ 0",
461 bidi.getLogicalRun(0).toString());
463 /* test REMOVE_BIDI_CONTROLS together with DO_MIRRORING */
464 bidi.setPara("abc\u200e", Bidi.LTR, null);
465 String out = bidi.writeReordered(Bidi.REMOVE_BIDI_CONTROLS | Bidi.DO_MIRRORING);
466 assertEquals("\nWrong result #1", "abc", out);
468 /* test inverse Bidi with marks and contextual orientation */
469 bidi.setReorderingMode(Bidi.REORDER_INVERSE_LIKE_DIRECT);
470 bidi.setReorderingOptions(Bidi.OPTION_INSERT_MARKS);
471 bidi.setPara("", Bidi.LEVEL_DEFAULT_RTL, null);
472 out = bidi.writeReordered(0);
473 assertEquals("\nWrong result #2", "", out);
474 bidi.setPara(" ", Bidi.LEVEL_DEFAULT_RTL, null);
475 out = bidi.writeReordered(0);
476 assertEquals("\nWrong result #3", " ", out);
477 bidi.setPara("abc", Bidi.LEVEL_DEFAULT_RTL, null);
478 out = bidi.writeReordered(0);
479 assertEquals("\nWrong result #4", "abc", out);
480 bidi.setPara("\u05d0\u05d1", Bidi.LEVEL_DEFAULT_RTL, null);
481 out = bidi.writeReordered(0);
482 assertEquals("\nWrong result #5", "\u05d1\u05d0", out);
483 bidi.setPara("abc \u05d0\u05d1", Bidi.LEVEL_DEFAULT_RTL, null);
484 out = bidi.writeReordered(0);
485 assertEquals("\nWrong result #6", "\u05d1\u05d0 abc", out);
486 bidi.setPara("\u05d0\u05d1 abc", Bidi.LEVEL_DEFAULT_RTL, null);
487 out = bidi.writeReordered(0);
488 assertEquals("\nWrong result #7", "\u200fabc \u05d1\u05d0", out);
489 bidi.setPara("\u05d0\u05d1 abc .-=", Bidi.LEVEL_DEFAULT_RTL, null);
490 out = bidi.writeReordered(0);
491 assertEquals("\nWrong result #8", "\u200f=-. abc \u05d1\u05d0", out);
492 bidi.orderParagraphsLTR(true);
493 bidi.setPara("\n\r \n\rabc\n\u05d0\u05d1\rabc \u05d2\u05d3\n\r" +
494 "\u05d4\u05d5 abc\n\u05d6\u05d7 abc .-=\r\n" +
495 "-* \u05d8\u05d9 abc .-=", Bidi.LEVEL_DEFAULT_RTL, null);
496 out = bidi.writeReordered(0);
497 assertEquals("\nWrong result #9",
498 "\n\r \n\rabc\n\u05d1\u05d0\r\u05d3\u05d2 abc\n\r" +
499 "\u200fabc \u05d5\u05d4\n\u200f=-. abc \u05d7\u05d6\r\n" +
500 "\u200f=-. abc \u05d9\u05d8 *-", out);
502 bidi.setPara("\u05d0 \t", Bidi.LTR, null);
503 out = bidi.writeReordered(0);
504 assertEquals("\nWrong result #10", "\u05D0\u200e \t", out);
505 bidi.setPara("\u05d0 123 \t\u05d1 123 \u05d2", Bidi.LTR, null);
506 out = bidi.writeReordered(0);
507 assertEquals("\nWrong result #11", "\u05d0 \u200e123\u200e \t\u05d2 123 \u05d1", out);
508 bidi.setPara("\u05d0 123 \u0660\u0661 ab", Bidi.LTR, null);
509 out = bidi.writeReordered(0);
510 assertEquals("\nWrong result #12", "\u05d0 \u200e123 \u200e\u0660\u0661 ab", out);
511 bidi.setPara("ab \t", Bidi.RTL, null);
512 out = bidi.writeReordered(0);
513 assertEquals("\nWrong result #13", "\u200f\t ab", out);
515 /* check exceeding para level */
517 bidi.setPara("A\u202a\u05d0\u202aC\u202c\u05d1\u202cE", (byte)(Bidi.MAX_EXPLICIT_LEVEL - 1), null);
518 assertEquals("\nWrong level at index 2", Bidi.MAX_EXPLICIT_LEVEL, bidi.getLevelAt(2));
520 /* check 1-char runs with RUNS_ONLY */
521 bidi.setReorderingMode(Bidi.REORDER_RUNS_ONLY);
522 bidi.setPara("a \u05d0 b \u05d1 c \u05d2 d ", Bidi.LTR, null);
523 assertEquals("\nWrong number of runs #4", 14, bidi.countRuns());
525 /* test testGetBaseDirection to verify fast string direction detection function */
526 /* mixed start with L */
527 String mixedEnglishFirst = "\u0061\u0627\u0032\u06f3\u0061\u0034";
528 assertEquals("\nWrong direction through fast detection #1", Bidi.LTR, Bidi.getBaseDirection(mixedEnglishFirst));
529 /* mixed start with AL */
530 String mixedArabicFirst = "\u0661\u0627\u0662\u06f3\u0061\u0664";
531 assertEquals("\nWrong direction through fast detection #2", Bidi.RTL, Bidi.getBaseDirection(mixedArabicFirst));
532 /* mixed Start with R */
533 String mixedHebrewFirst = "\u05EA\u0627\u0662\u06f3\u0061\u0664";
534 assertEquals("\nWrong direction through fast detection #3", Bidi.RTL, Bidi.getBaseDirection(mixedHebrewFirst));
535 /* all AL (Arabic. Persian) */
536 String persian = "\u0698\u067E\u0686\u06AF";
537 assertEquals("\nWrong direction through fast detection #4", Bidi.RTL, Bidi.getBaseDirection(persian));
538 /* all R (Hebrew etc.) */
539 String hebrew = "\u0590\u05D5\u05EA\u05F1";
540 assertEquals("\nWrong direction through fast detection #5", Bidi.RTL, Bidi.getBaseDirection(hebrew));
541 /* all L (English) */
542 String english = "\u0071\u0061\u0066";
543 assertEquals("\nWrong direction through fast detection #6", Bidi.LTR, Bidi.getBaseDirection(english));
544 /* mixed start with weak AL an then L */
545 String startWeakAL = "\u0663\u0071\u0061\u0066";
546 assertEquals("\nWrong direction through fast detection #7", Bidi.LTR, Bidi.getBaseDirection(startWeakAL));
547 /* mixed start with weak L and then AL */
548 String startWeakL = "\u0031\u0698\u067E\u0686\u06AF";
549 assertEquals("\nWrong direction through fast detection #8", Bidi.RTL, Bidi.getBaseDirection(startWeakL));
552 assertEquals("\nWrong direction through fast detection #9", Bidi.NEUTRAL, Bidi.getBaseDirection(empty));
553 /* surrogate character */
554 String surrogateChar = "\uD800\uDC00";
555 assertEquals("\nWrong direction through fast detection #10", Bidi.LTR, Bidi.getBaseDirection(surrogateChar));
556 /* all weak L (English digits) */
557 String allEnglishDigits = "\u0031\u0032\u0033";
558 assertEquals("\nWrong direction through fast detection #11", Bidi.NEUTRAL, Bidi.getBaseDirection(allEnglishDigits));
559 /* all weak AL (Arabic digits) */
560 String allArabicDigits = "\u0663\u0664\u0665";
561 assertEquals("\nWrong direction through fast detection #12", Bidi.NEUTRAL, Bidi.getBaseDirection(allArabicDigits));
563 String nullString = null;
564 assertEquals("\nWrong direction through fast detection #13", Bidi.NEUTRAL, Bidi.getBaseDirection(nullString));
565 /* first L (English) others are R (Hebrew etc.) */
566 String startEnglishOthersHebrew = "\u0071\u0590\u05D5\u05EA\u05F1";
567 assertEquals("\nWrong direction through fast detection #14", Bidi.LTR, Bidi.getBaseDirection(startEnglishOthersHebrew));
568 /* last R (Hebrew etc.) others are weak L (English Digits) */
569 String lastHebrewOthersEnglishDigit = "\u0031\u0032\u0033\u05F1";
570 assertEquals("\nWrong direction through fast detection #15", Bidi.RTL, Bidi.getBaseDirection(lastHebrewOthersEnglishDigit));
574 public static void main(String[] args) {
576 new TestBidi().run(args);
578 catch (Exception e) {
579 System.out.println(e);