]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/test/cintltst/cbiditst.c
ICU-511.25.tar.gz
[apple/icu.git] / icuSources / test / cintltst / cbiditst.c
index bf7dab2d74eaceaecc2d0adc28107735eccd2022..a43dcd745f3b40f03ad943356cf0084f6b087043 100644 (file)
@@ -1,6 +1,6 @@
 /********************************************************************
  * COPYRIGHT:
- * Copyright (c) 1997-2006, International Business Machines Corporation and
+ * Copyright (c) 1997-2012, International Business Machines Corporation and
  * others. All Rights Reserved.
  ********************************************************************/
 /*   file name:  cbiditst.cpp
@@ -9,7 +9,7 @@
 *   indentation:4
 *
 *   created on: 1999sep27
-*   created by: Markus W. Scherer
+*   created by: Markus W. Scherer, updated by Matitiahu Allouche
 */
 
 #include "cintltst.h"
 
 /* prototypes ---------------------------------------------------------------*/
 
-static void
-charFromDirPropTest(void);
+void addComplexTest(TestNode** root);
 
-static void
-doBiDiTest(void);
+static void testCharFromDirProp(void);
 
-static void
-doTests(UBiDi *pBiDi, UBiDi *pLine, UBool countRunsFirst);
+static void testBidi(void);
 
-static void
-doTest(UBiDi *pBiDi, int testNumber, BiDiTestData *test, int32_t lineStart, UBool countRunsFirst);
+static void doTests(UBiDi *pBiDi, UBiDi *pLine, UBool countRunsFirst);
 
-static void
-testReordering(UBiDi *pBiDi, int testNumber);
+static void doMisc(void);
 
-static void
-doInverseBiDiTest(void);
+static void doTest(UBiDi *pBiDi, int testNumber, const BiDiTestData *test,
+                   int32_t lineStart, UBool countRunsFirst);
 
-static void
-testManyInverseBiDi(UBiDi *pBiDi, UBiDiLevel direction);
+static void _testReordering(UBiDi *pBiDi, int testNumber);
 
-static void
-testInverseBiDi(UBiDi *pBiDi, const UChar *src, int32_t srcLength, UBiDiLevel direction, UErrorCode *pErrorCode);
+static void testInverse(void);
 
-static void
-testWriteReverse(void);
+static void _testManyInverseBidi(UBiDi *pBiDi, UBiDiLevel direction);
 
-static void
-doArabicShapingTest(void);
+static void _testInverseBidi(UBiDi *pBiDi, const UChar *src, int32_t srcLength,
+                             UBiDiLevel direction, UErrorCode *pErrorCode);
 
-static void
-doLamAlefSpecialVLTRArabicShapingTest(void);
+static void _testWriteReverse(void);
 
-static void
-doTashkeelSpecialVLTRArabicShapingTest(void);
+static void _testManyAddedPoints(void);
 
-static void
-doLOGICALArabicDeShapingTest(void);
+static void _testMisc(void);
+
+static void doArabicShapingTest(void);
 
-static void TestReorder(void);
+static void doLamAlefSpecialVLTRArabicShapingTest(void);
 
-static void TestFailureRecovery(void);
+static void doTashkeelSpecialVLTRArabicShapingTest(void);
 
-static void TestMultipleParagraphs(void);
+static void doLOGICALArabicDeShapingTest(void);
+
+static void doArabicShapingTestForBug5421(void);
+
+static void doArabicShapingTestForBug8703(void);
+
+static void doArabicShapingTestForBug9024(void);
+
+static void testReorder(void);
+
+static void testReorderArabicMathSymbols(void);
+
+static void testFailureRecovery(void);
+
+static void testMultipleParagraphs(void);
+
+static void testGetBaseDirection(void);
+
+static void testContext(void);
+
+static void doTailTest(void);
 
 /* new BIDI API */
-static void doReorderingModeBidiTest(void);
-static void doReorderRunsTest(void);
-static void doBidiStreamingTest(void);
-static void doBidiClassOverrideTest(void);
-static const char* inverseBasic(UBiDi *pBiDi, const UChar *src, int32_t srcLen,
+static void testReorderingMode(void);
+static void testReorderRunsOnly(void);
+static void testStreaming(void);
+static void testClassOverride(void);
+static const char* inverseBasic(UBiDi *pBiDi, const char *src, int32_t srcLen,
                                 uint32_t option, UBiDiLevel level, char *result);
 static UBool assertRoundTrip(UBiDi *pBiDi, int32_t tc, int32_t outIndex,
                              const char *srcChars, const char *destChars,
                              const UChar *dest, int32_t destLen, int mode,
                              int option, UBiDiLevel level);
 static UBool checkResultLength(UBiDi *pBiDi, const char *srcChars,
-                               const char *destChars, const UChar *dest,
+                               const char *destChars,
                                int32_t destLen, const char *mode,
                                const char *option, UBiDiLevel level);
-static UBool testMaps(UBiDi *pBiDi, int32_t stringIndex, const char *src,
-                      const char *dest, const char *mode, const char* option,
-                      UBiDiLevel level, UBool forward);
+static UBool checkMaps(UBiDi *pBiDi, int32_t stringIndex, const char *src,
+                       const char *dest, const char *mode, const char* option,
+                       UBiDiLevel level, UBool forward);
 
 /* helpers ------------------------------------------------------------------ */
 
 static const char *levelString="...............................................................";
 
-static void
-initCharFromDirProps(void);
+static void initCharFromDirProps(void);
 
 static UChar *
 getStringFromDirProps(const uint8_t *dirProps, int32_t length, UChar *buffer);
 
-static void
-printUnicode(const UChar *s, int32_t length, const UBiDiLevel *levels);
+static void printUnicode(const UChar *s, int32_t length, const UBiDiLevel *levels);
 
 /* regression tests ---------------------------------------------------------*/
 
-void addComplexTest(TestNode** root);
-
 void
 addComplexTest(TestNode** root) {
-    addTest(root, charFromDirPropTest, "complex/bidi/charFromDirPropTest");
-    addTest(root, doBiDiTest, "complex/bidi/BiDiTest");
-    addTest(root, doInverseBiDiTest, "complex/bidi/inverse");
-    addTest(root, TestReorder,"complex/bidi/TestReorder");
-    addTest(root, TestFailureRecovery,"complex/bidi/TestFailureRecovery");
-    addTest(root, TestMultipleParagraphs,"complex/bidi/multipleParagraphs");
-    addTest(root, doReorderingModeBidiTest, "complex/bidi/TestReorderingMode");
-    addTest(root, doReorderRunsTest, "complex/bidi/TestReorderRunsOnly");
-    addTest(root, doBidiStreamingTest, "complex/bidi/TestStreamingMode");
-    addTest(root, doBidiClassOverrideTest, "complex/bidi/TestClassOverride");
+    addTest(root, testCharFromDirProp, "complex/bidi/TestCharFromDirProp");
+    addTest(root, testBidi, "complex/bidi/TestBidi");
+    addTest(root, testInverse, "complex/bidi/TestInverse");
+    addTest(root, testReorder,"complex/bidi/TestReorder");
+    addTest(root, testFailureRecovery,"complex/bidi/TestFailureRecovery");
+    addTest(root, testMultipleParagraphs,"complex/bidi/TestMultipleParagraphs");
+    addTest(root, testReorderingMode, "complex/bidi/TestReorderingMode");
+    addTest(root, testReorderRunsOnly, "complex/bidi/TestReorderRunsOnly");
+    addTest(root, testStreaming, "complex/bidi/TestStreaming");
+    addTest(root, testClassOverride, "complex/bidi/TestClassOverride");
+    addTest(root, testGetBaseDirection, "complex/bidi/testGetBaseDirection");
+    addTest(root, testContext, "complex/bidi/testContext");
 
     addTest(root, doArabicShapingTest, "complex/arabic-shaping/ArabicShapingTest");
     addTest(root, doLamAlefSpecialVLTRArabicShapingTest, "complex/arabic-shaping/lamalef");
     addTest(root, doTashkeelSpecialVLTRArabicShapingTest, "complex/arabic-shaping/tashkeel");
     addTest(root, doLOGICALArabicDeShapingTest, "complex/arabic-shaping/unshaping");
+    addTest(root, doArabicShapingTestForBug5421, "complex/arabic-shaping/bug-5421");
+    addTest(root, doTailTest, "complex/arabic-shaping/tailtest");
+    addTest(root, doArabicShapingTestForBug8703, "complex/arabic-shaping/bug-8703");
+    addTest(root, testReorderArabicMathSymbols, "complex/bidi/bug-9024");
+    addTest(root, doArabicShapingTestForBug9024, "complex/arabic-shaping/bug-9024");
 }
 
-/* verify that the exemplar characters have the expected bidi classes */
 static void
-charFromDirPropTest(void) {
+testCharFromDirProp(void) {
+    /* verify that the exemplar characters have the expected bidi classes */
     int32_t i;
 
+    log_verbose("\nEntering TestCharFromDirProp\n\n");
     initCharFromDirProps();
 
     for(i=0; i<U_CHAR_DIRECTION_COUNT; ++i) {
@@ -141,14 +156,15 @@ charFromDirPropTest(void) {
                     i, charFromDirProp[i], u_charDirection(charFromDirProp[i]), i);
         }
     }
+    log_verbose("\nExiting TestCharFromDirProp\n\n");
 }
 
 static void
-doBiDiTest() {
+testBidi(void) {
     UBiDi *pBiDi, *pLine=NULL;
     UErrorCode errorCode=U_ZERO_ERROR;
 
-    log_verbose("\n*** bidi regression test ***\n");
+    log_verbose("\nEntering TestBidi\n\n");
 
     pBiDi=ubidi_openSized(MAXLEN, 0, &errorCode);
     if(pBiDi!=NULL) {
@@ -162,6 +178,7 @@ doBiDiTest() {
     } else {
         log_err("ubidi_openSized() returned NULL, errorCode %s\n", myErrorName(errorCode));
     }
+    doMisc();
 
     if(pLine!=NULL) {
         ubidi_close(pLine);
@@ -170,47 +187,47 @@ doBiDiTest() {
         ubidi_close(pBiDi);
     }
 
-    log_verbose("\n*** bidi regression test finished ***\n");
+    log_verbose("\nExiting TestBidi\n\n");
 }
 
 static void
 doTests(UBiDi *pBiDi, UBiDi *pLine, UBool countRunsFirst) {
-    int i;
+    int testNumber;
     UChar string[MAXLEN];
     UErrorCode errorCode;
     int32_t lineStart;
     UBiDiLevel paraLevel;
 
-    for(i=0; i<bidiTestCount; ++i) {
+    for(testNumber=0; testNumber<bidiTestCount; ++testNumber) {
         errorCode=U_ZERO_ERROR;
-        getStringFromDirProps(tests[i].text, tests[i].length, string);
-        paraLevel=tests[i].paraLevel;
+        getStringFromDirProps(tests[testNumber].text, tests[testNumber].length, string);
+        paraLevel=tests[testNumber].paraLevel;
         ubidi_setPara(pBiDi, string, -1, paraLevel, NULL, &errorCode);
         if(U_SUCCESS(errorCode)) {
             log_verbose("ubidi_setPara(tests[%d], paraLevel %d) ok, direction %d paraLevel=%d\n",
-                    i, paraLevel, ubidi_getDirection(pBiDi), ubidi_getParaLevel(pBiDi));
-            lineStart=tests[i].lineStart;
+                    testNumber, paraLevel, ubidi_getDirection(pBiDi), paraLevel);
+            lineStart=tests[testNumber].lineStart;
             if(lineStart==-1) {
-                doTest(pBiDi, i, tests+i, 0, countRunsFirst);
+                doTest(pBiDi, testNumber, tests+testNumber, 0, countRunsFirst);
             } else {
-                ubidi_setLine(pBiDi, lineStart, tests[i].lineLimit, pLine, &errorCode);
+                ubidi_setLine(pBiDi, lineStart, tests[testNumber].lineLimit, pLine, &errorCode);
                 if(U_SUCCESS(errorCode)) {
                     log_verbose("ubidi_setLine(%d, %d) ok, direction %d paraLevel=%d\n",
-                            lineStart, tests[i].lineLimit, ubidi_getDirection(pLine), ubidi_getParaLevel(pLine));
-                    doTest(pLine, i, tests+i, lineStart, countRunsFirst);
+                            lineStart, tests[testNumber].lineLimit, ubidi_getDirection(pLine), ubidi_getParaLevel(pLine));
+                    doTest(pLine, testNumber, tests+testNumber, lineStart, countRunsFirst);
                 } else {
                     log_err("ubidi_setLine(tests[%d], %d, %d) failed with errorCode %s\n",
-                            i, lineStart, tests[i].lineLimit, myErrorName(errorCode));
+                            testNumber, lineStart, tests[testNumber].lineLimit, myErrorName(errorCode));
                 }
             }
         } else {
             log_err("ubidi_setPara(tests[%d], paraLevel %d) failed with errorCode %s\n",
-                    i, paraLevel, myErrorName(errorCode));
+                    testNumber, paraLevel, myErrorName(errorCode));
         }
     }
 }
 
-static const char columns[62] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+static const char columns[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 
 #define TABLE_SIZE  256
 static UBool   tablesInitialized = FALSE;
@@ -229,7 +246,8 @@ static void buildPseudoTables(void)
     - @ == LRM
     - & == RLM
     - A-F == Arabic Letters 0631-0636
-    - G-Z == Hebrew letters 05d7-05ea
+    - G-V == Hebrew letters 05d7-05e6
+    - W-Z == Unassigned RTL 08d0-08d3
     - 0-5 == western digits 0030-0035
     - 6-9 == Arabic-Indic digits 0666-0669
     - ` == Combining Grave Accent 0300 (NSM)
@@ -304,7 +322,13 @@ static void buildPseudoTables(void)
         UCharToPseud2[uchar & 0x00ff] = c;
     }
     /* initialize Hebrew letters */
-    for (i = 16, uchar = 0x05D7; i < 36; i++, uchar++) {
+    for (i = 16, uchar = 0x05D7; i < 32; i++, uchar++) {
+        c = (uint8_t)columns[i];
+        pseudoToUChar[c] = uchar;
+        UCharToPseud2[uchar & 0x00ff] = c;
+    }
+    /* initialize Unassigned code points */
+    for (i = 32, uchar=0x08D0; i < 36; i++, uchar++) {
         c = (uint8_t)columns[i];
         pseudoToUChar[c] = uchar;
         UCharToPseud2[uchar & 0x00ff] = c;
@@ -320,7 +344,7 @@ static void buildPseudoTables(void)
 
 /*----------------------------------------------------------------------*/
 
-static int pseudoToU16( const int length, const char * input, UChar * output )
+static int pseudoToU16(const int length, const char * input, UChar * output)
 /*  This function converts a pseudo-Bidi string into a UChar string.
     It returns the length of the UChar string.
 */
@@ -331,12 +355,13 @@ static int pseudoToU16( const int length, const char * input, UChar * output )
     }
     for (i = 0; i < length; i++)
         output[i] = pseudoToUChar[(uint8_t)input[i]];
+    output[length] = 0;
     return length;
 }
 
 /*----------------------------------------------------------------------*/
 
-static int u16ToPseudo( const int length, const UChar * input, char * output )
+static int u16ToPseudo(const int length, const UChar * input, char * output)
 /*  This function converts a UChar string into a pseudo-Bidi string.
     It returns the length of the pseudo-Bidi string.
 */
@@ -369,7 +394,7 @@ static char * formatLevels(UBiDi *bidi, char *buffer) {
     }
     for (i=0; i<len; i++) {
         k = gotLevels[i];
-        if (k >= sizeof columns)
+        if (k >= sizeof(columns))
             c = '+';
         else
             c = columns[k];
@@ -378,8 +403,236 @@ static char * formatLevels(UBiDi *bidi, char *buffer) {
     buffer[len] = '\0';
     return buffer;
 }
+static const char *reorderingModeNames[] = {
+    "UBIDI_REORDER_DEFAULT",
+    "UBIDI_REORDER_NUMBERS_SPECIAL",
+    "UBIDI_REORDER_GROUP_NUMBERS_WITH_R",
+    "UBIDI_REORDER_RUNS_ONLY",
+    "UBIDI_REORDER_INVERSE_NUMBERS_AS_L",
+    "UBIDI_REORDER_INVERSE_LIKE_DIRECT",
+    "UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL"};
+
+static char *reorderingOptionNames(char *buffer, int options) {
+    buffer[0] = 0;
+    if (options & UBIDI_OPTION_INSERT_MARKS) {
+        strcat(buffer, " UBIDI_OPTION_INSERT_MARKS");
+    }
+    if (options & UBIDI_OPTION_REMOVE_CONTROLS) {
+        strcat(buffer, " UBIDI_OPTION_REMOVE_CONTROLS");
+    }
+    if (options & UBIDI_OPTION_STREAMING) {
+        strcat(buffer, " UBIDI_OPTION_STREAMING");
+    }
+    return buffer;
+}
 
-static void TestReorder(){
+static void printCaseInfo(UBiDi *bidi, const char *src, const char *dst)
+/* src and dst are char arrays encoded as pseudo Bidi */
+{
+    /* Since calls to log_err with a \n within the pattern increment the
+     * error count, new lines are issued via fputs, except when we want the
+     * increment to happen.
+     */
+    UErrorCode errorCode=U_ZERO_ERROR;
+    int32_t i, length = ubidi_getProcessedLength(bidi);
+    const UBiDiLevel *levels;
+    char levelChars[MAXLEN];
+    UBiDiLevel lev;
+    int32_t runCount;
+    char buffer[100];
+    log_err("========================================"); fputs("\n", stderr);
+    levels = ubidi_getLevels(bidi, &errorCode);
+    if (U_FAILURE(errorCode)) {
+        strcpy(levelChars, "BAD LEVELS");
+    } else {
+        log_err("Processed length: %d", length); fputs("\n", stderr);
+        for (i = 0; i < length; i++) {
+            lev = levels[i];
+            if (lev < sizeof(columns)) {
+                levelChars[i] = columns[lev];
+            } else {
+                levelChars[i] = '+';
+            }
+        }
+        levelChars[length] = 0;
+    }
+    log_err("Levels: %s", levelChars); fputs("\n", stderr);
+    log_err("Source: %s", src); fputs("\n", stderr);
+    log_err("Result: %s", dst); fputs("\n", stderr);
+    log_err("Direction: %d", ubidi_getDirection(bidi)); fputs("\n", stderr);
+    log_err("paraLevel: %d", ubidi_getParaLevel(bidi)); fputs("\n", stderr);
+    i = ubidi_getReorderingMode(bidi);
+    log_err("reorderingMode: %d = %s", i, reorderingModeNames[i]);
+    fputs("\n", stderr);
+    i = ubidi_getReorderingOptions(bidi);
+    log_err("reorderingOptions: %d = %s", i, reorderingOptionNames(buffer, i));
+    fputs("\n", stderr);
+    runCount = ubidi_countRuns(bidi, &errorCode);
+    if (U_FAILURE(errorCode)) {
+        log_err( "BAD RUNS");
+    } else {
+        log_err("Runs: %d => logicalStart.length/level: ", runCount);
+        for (i = 0; i < runCount; i++) {
+            UBiDiDirection dir;
+            int32_t start, len;
+            dir = ubidi_getVisualRun(bidi, i, &start, &len);
+            log_err(" %d.%d/%d", start, len, dir);
+        }
+    }
+    fputs("\n", stderr);
+}
+
+static UBool matchingPair(UBiDi *bidi, int32_t i, char c1, char c2)
+{
+    /* No test for []{} since they have special meaning for pseudo Bidi */
+    static char mates1Chars[] = "<>()";
+    static char mates2Chars[] = "><)(";
+    UBiDiLevel level;
+    int k, len;
+
+    if (c1 == c2) {
+        return TRUE;
+    }
+    /* For UBIDI_REORDER_RUNS_ONLY, it would not be correct to check levels[i],
+       so we use the appropriate run's level, which is good for all cases.
+     */
+    ubidi_getLogicalRun(bidi, i, NULL, &level);
+    if ((level & 1) == 0) {
+        return FALSE;
+    }
+    len = strlen(mates1Chars);
+    for (k = 0; k < len; k++) {
+        if ((c1 == mates1Chars[k]) && (c2 == mates2Chars[k])) {
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+static UBool checkWhatYouCan(UBiDi *bidi, const char *srcChars, const char *dstChars)
+/* srcChars and dstChars are char arrays encoded as pseudo Bidi */
+{
+    int32_t i, idx, logLimit, visLimit;
+    UBool testOK, errMap, errDst;
+    UErrorCode errorCode=U_ZERO_ERROR;
+    int32_t visMap[MAXLEN];
+    int32_t logMap[MAXLEN];
+    char accumSrc[MAXLEN];
+    char accumDst[MAXLEN];
+    ubidi_getVisualMap(bidi, visMap, &errorCode);
+    ubidi_getLogicalMap(bidi, logMap, &errorCode);
+    if (U_FAILURE(errorCode)) {
+        log_err("Error #1 invoking ICU within checkWhatYouCan\n");
+        return FALSE;
+    }
+
+    testOK = TRUE;
+    errMap = errDst = FALSE;
+    logLimit = ubidi_getProcessedLength(bidi);
+    visLimit = ubidi_getResultLength(bidi);
+    memset(accumSrc, '?', logLimit);
+    memset(accumDst, '?', visLimit);
+
+    for (i = 0; i < logLimit; i++) {
+        idx = ubidi_getVisualIndex(bidi, i, &errorCode);
+        if (idx != logMap[i]) {
+            errMap = TRUE;
+        }
+        if (idx == UBIDI_MAP_NOWHERE) {
+            continue;
+        }
+        if (idx >= visLimit) {
+            continue;
+        }
+        accumDst[idx] = srcChars[i];
+        if (!matchingPair(bidi, i, srcChars[i], dstChars[idx])) {
+            errDst = TRUE;
+        }
+    }
+    accumDst[visLimit] = 0;
+    if (U_FAILURE(errorCode)) {
+        log_err("Error #2 invoking ICU within checkWhatYouCan\n");
+        return FALSE;
+    }
+    if (errMap) {
+        if (testOK) {
+            printCaseInfo(bidi, srcChars, dstChars);
+            testOK = FALSE;
+        }
+        log_err("Mismatch between getLogicalMap() and getVisualIndex()\n");
+        log_err("Map    :");
+        for (i = 0; i < logLimit; i++) {
+            log_err(" %d", logMap[i]);
+        }
+        fputs("\n", stderr);
+        log_err("Indexes:");
+        for (i = 0; i < logLimit; i++) {
+            log_err(" %d", ubidi_getVisualIndex(bidi, i, &errorCode));
+        }
+        fputs("\n", stderr);
+    }
+    if (errDst) {
+        if (testOK) {
+            printCaseInfo(bidi, srcChars, dstChars);
+            testOK = FALSE;
+        }
+        log_err("Source does not map to Result\n");
+        log_err("We got: %s", accumDst); fputs("\n", stderr);
+    }
+
+    errMap = errDst = FALSE;
+    for (i = 0; i < visLimit; i++) {
+        idx = ubidi_getLogicalIndex(bidi, i, &errorCode);
+        if (idx != visMap[i]) {
+            errMap = TRUE;
+        }
+        if (idx == UBIDI_MAP_NOWHERE) {
+            continue;
+        }
+        if (idx >= logLimit) {
+            continue;
+        }
+        accumSrc[idx] = dstChars[i];
+        if (!matchingPair(bidi, idx, srcChars[idx], dstChars[i])) {
+            errDst = TRUE;
+        }
+    }
+    accumSrc[logLimit] = 0;
+    if (U_FAILURE(errorCode)) {
+        log_err("Error #3 invoking ICU within checkWhatYouCan\n");
+        return FALSE;
+    }
+    if (errMap) {
+        if (testOK) {
+            printCaseInfo(bidi, srcChars, dstChars);
+            testOK = FALSE;
+        }
+        log_err("Mismatch between getVisualMap() and getLogicalIndex()\n");
+        log_err("Map    :");
+        for (i = 0; i < visLimit; i++) {
+            log_err(" %d", visMap[i]);
+        }
+        fputs("\n", stderr);
+        log_err("Indexes:");
+        for (i = 0; i < visLimit; i++) {
+            log_err(" %d", ubidi_getLogicalIndex(bidi, i, &errorCode));
+        }
+        fputs("\n", stderr);
+    }
+    if (errDst) {
+        if (testOK) {
+            printCaseInfo(bidi, srcChars, dstChars);
+            testOK = FALSE;
+        }
+        log_err("Result does not map to Source\n");
+        log_err("We got: %s", accumSrc);
+        fputs("\n", stderr);
+    }
+    return testOK;
+}
+
+static void
+testReorder(void) {
     static const char* const logicalOrder[] ={
             "del(KC)add(K.C.&)",
             "del(QDVT) add(BVDL)",
@@ -392,6 +645,7 @@ static void TestReorder(){
             "day  4   I  DPIQNF    dayabbr",
             "day  5  M  DPMEG  dayabbr",
             "helloDPMEG",
+            "hello WXYZ"
     };
     static const char* const visualOrder[]={
             "del(CK)add(&.C.K)",
@@ -405,6 +659,7 @@ static void TestReorder(){
             "day  4   FNQIPD  I    dayabbr",
             "day  5  GEMPD  M  dayabbr",
             "helloGEMPD",
+            "hello ZYXW"
     };
     static const char* const visualOrder1[]={
             ")K.C.&(dda)KC(led",
@@ -418,6 +673,7 @@ static void TestReorder(){
             "rbbayad    I  DPIQNF   4  yad",
             "rbbayad  M  DPMEG  5  yad",
             "DPMEGolleh",
+            "WXYZ olleh"
     };
 
     static const char* const visualOrder2[]={
@@ -432,6 +688,7 @@ static void TestReorder(){
             "rbbayad    @I  DPIQNF@   4  yad",
             "rbbayad  @M  DPMEG@  5  yad",
             "DPMEGolleh",
+            "WXYZ@ olleh"
     };
     static const char* const visualOrder3[]={
             ")K.C.&(KC)dda(led",
@@ -444,7 +701,8 @@ static void TestReorder(){
             "rbbayad  DPJQVM   J  3 yad",
             "rbbayad    DPIQNF     I 4 yad",
             "rbbayad  DPMEG   M  5 yad",
-            "DPMEGolleh"
+            "DPMEGolleh",
+            "WXYZ olleh"
     };
     static const char* const visualOrder4[]={
             "del(add(CK(.C.K)",
@@ -457,18 +715,23 @@ static void TestReorder(){
             "day 3  J   MVQJPD  dayabbr",
             "day 4 I     FNQIPD    dayabbr",
             "day 5  M   GEMPD  dayabbr",
-            "helloGEMPD"
+            "helloGEMPD",
+            "hello ZYXW"
     };
     char formatChars[MAXLEN];
     UErrorCode ec = U_ZERO_ERROR;
     UBiDi* bidi = ubidi_open();
     int i;
+
+    log_verbose("\nEntering TestReorder\n\n");
+
     for(i=0;i<LENGTHOF(logicalOrder);i++){
         int32_t srcSize = (int32_t)strlen(logicalOrder[i]);
         int32_t destSize = srcSize*2;
         UChar src[MAXLEN];
         UChar dest[MAXLEN];
         char chars[MAXLEN];
+        log_verbose("Testing L2V #1 for case %d\n", i);
         pseudoToU16(srcSize,logicalOrder[i],src);
         ec = U_ZERO_ERROR;
         ubidi_setPara(bidi,src,srcSize,UBIDI_DEFAULT_LTR ,NULL,&ec);
@@ -494,6 +757,7 @@ static void TestReorder(){
                     "Input   : %s\nExpected: %s\nGot     : %s\nLevels  : %s\nAt Index: %d\n",
                     logicalOrder[i],visualOrder[i],chars,formatLevels(bidi, formatChars),i);
         }
+        checkWhatYouCan(bidi, logicalOrder[i], chars);
     }
 
     for(i=0;i<LENGTHOF(logicalOrder);i++){
@@ -502,6 +766,7 @@ static void TestReorder(){
         UChar src[MAXLEN];
         UChar dest[MAXLEN];
         char chars[MAXLEN];
+        log_verbose("Testing L2V #2 for case %d\n", i);
         pseudoToU16(srcSize,logicalOrder[i],src);
         ec = U_ZERO_ERROR;
         ubidi_setPara(bidi,src,srcSize,UBIDI_DEFAULT_LTR ,NULL,&ec);
@@ -535,6 +800,7 @@ static void TestReorder(){
         UChar src[MAXLEN];
         UChar dest[MAXLEN];
         char chars[MAXLEN];
+        log_verbose("Testing V2L #3 for case %d\n", i);
         pseudoToU16(srcSize,logicalOrder[i],src);
         ec = U_ZERO_ERROR;
         ubidi_setInverse(bidi,TRUE);
@@ -566,6 +832,7 @@ static void TestReorder(){
         UChar dest[MAXLEN];
         char chars[MAXLEN];
         UBiDiLevel levels[UBIDI_MAX_EXPLICIT_LEVEL]={1,2,3,4,5,6,7,8,9,10};
+        log_verbose("Testing V2L #4 for case %d\n", i);
         pseudoToU16(srcSize,logicalOrder[i],src);
         ec = U_ZERO_ERROR;
         ubidi_setPara(bidi,src,srcSize,UBIDI_DEFAULT_LTR,levels,&ec);
@@ -599,6 +866,7 @@ static void TestReorder(){
         UChar dest[MAXLEN];
         char chars[MAXLEN];
         UBiDiLevel levels[UBIDI_MAX_EXPLICIT_LEVEL]={1,2,3,4,5,6,7,8,9,10};
+        log_verbose("Testing V2L #5 for case %d\n", i);
         pseudoToU16(srcSize,logicalOrder[i],src);
         ec = U_ZERO_ERROR;
         ubidi_setPara(bidi,src,srcSize,UBIDI_DEFAULT_LTR,levels,&ec);
@@ -622,10 +890,140 @@ static void TestReorder(){
         }
     }
     ubidi_close(bidi);
+
+    log_verbose("\nExiting TestReorder\n\n");
 }
 
 static void
-doTest(UBiDi *pBiDi, int testNumber, BiDiTestData *test, int32_t lineStart, UBool countRunsFirst) {
+testReorderArabicMathSymbols(void) {
+    static const UChar logicalOrder[][MAXLEN]={
+        /* Arabic mathematical Symbols 0x1EE00 - 0x1EE1B */
+        {0xD83B, 0xDE00, 0xD83B, 0xDE01, 0xD83B, 0xDE02, 0xD83B, 0xDE03, 0x20, 
+        0xD83B, 0xDE24, 0xD83B, 0xDE05, 0xD83B, 0xDE06, 0x20, 
+        0xD83B, 0xDE07, 0xD83B, 0xDE08, 0xD83B, 0xDE09, 0x20, 
+        0xD83B, 0xDE0A, 0xD83B, 0xDE0B, 0xD83B, 0xDE0C, 0xD83B, 0xDE0D, 0x20, 
+        0xD83B, 0xDE0E, 0xD83B, 0xDE0F, 0xD83B, 0xDE10, 0xD83B, 0xDE11, 0x20, 
+        0xD83B, 0xDE12, 0xD83B, 0xDE13, 0xD83B, 0xDE14, 0xD83B, 0xDE15, 0x20, 
+        0xD83B, 0xDE16, 0xD83B, 0xDE17, 0xD83B, 0xDE18, 0x20, 
+        0xD83B, 0xDE19, 0xD83B, 0xDE1A, 0xD83B, 0xDE1B},
+        /* Arabic mathematical Symbols - Looped Symbols, 0x1EE80 - 0x1EE9B */
+        {0xD83B, 0xDE80, 0xD83B, 0xDE81, 0xD83B, 0xDE82, 0xD83B, 0xDE83, 0x20, 
+        0xD83B, 0xDE84, 0xD83B, 0xDE85, 0xD83B, 0xDE86, 0x20, 
+        0xD83B, 0xDE87, 0xD83B, 0xDE88, 0xD83B, 0xDE89, 0x20, 
+        0xD83B, 0xDE8B, 0xD83B, 0xDE8C, 0xD83B, 0xDE8D, 0x20, 
+        0xD83B, 0xDE8E, 0xD83B, 0xDE8F, 0xD83B, 0xDE90, 0xD83B, 0xDE91, 0x20, 
+        0xD83B, 0xDE92, 0xD83B, 0xDE93, 0xD83B, 0xDE94, 0xD83B, 0xDE95, 0x20, 
+        0xD83B, 0xDE96, 0xD83B, 0xDE97, 0xD83B, 0xDE98, 0x20, 
+        0xD83B, 0xDE99, 0xD83B, 0xDE9A, 0xD83B, 0xDE9B},
+        /* Arabic mathematical Symbols - Double-struck Symbols, 0x1EEA1 - 0x1EEBB */
+        {0xD83B, 0xDEA1, 0xD83B, 0xDEA2, 0xD83B, 0xDEA3, 0x20, 
+        0xD83B, 0xDEA5, 0xD83B, 0xDEA6, 0x20, 
+        0xD83B, 0xDEA7, 0xD83B, 0xDEA8, 0xD83B, 0xDEA9, 0x20, 
+        0xD83B, 0xDEAB, 0xD83B, 0xDEAC, 0xD83B, 0xDEAD, 0x20, 
+        0xD83B, 0xDEAE, 0xD83B, 0xDEAF, 0xD83B, 0xDEB0, 0xD83B, 0xDEB1, 0x20, 
+        0xD83B, 0xDEB2, 0xD83B, 0xDEB3, 0xD83B, 0xDEB4, 0xD83B, 0xDEB5, 0x20, 
+        0xD83B, 0xDEB6, 0xD83B, 0xDEB7, 0xD83B, 0xDEB8, 0x20, 
+        0xD83B, 0xDEB9, 0xD83B, 0xDEBA, 0xD83B, 0xDEBB},
+        /* Arabic mathematical Symbols - Initial Symbols, 0x1EE21 - 0x1EE3B */
+        {0xD83B, 0xDE21, 0xD83B, 0xDE22, 0x20, 
+        0xD83B, 0xDE27, 0xD83B, 0xDE29, 0x20, 
+        0xD83B, 0xDE2A, 0xD83B, 0xDE2B, 0xD83B, 0xDE2C, 0xD83B, 0xDE2D, 0x20, 
+        0xD83B, 0xDE2E, 0xD83B, 0xDE2F, 0xD83B, 0xDE30, 0xD83B, 0xDE31, 0x20, 
+        0xD83B, 0xDE32, 0xD83B, 0xDE34, 0xD83B, 0xDE35, 0x20, 
+        0xD83B, 0xDE36, 0xD83B, 0xDE37, 0x20, 
+        0xD83B, 0xDE39, 0xD83B, 0xDE3B},
+        /* Arabic mathematical Symbols - Tailed Symbols */
+        {0xD83B, 0xDE42, 0xD83B, 0xDE47, 0xD83B, 0xDE49, 0xD83B, 0xDE4B, 0x20, 
+        0xD83B, 0xDE4D, 0xD83B, 0xDE4E, 0xD83B, 0xDE4F, 0x20, 
+        0xD83B, 0xDE51, 0xD83B, 0xDE52, 0xD83B, 0xDE54, 0xD83B, 0xDE57, 0x20, 
+        0xD83B, 0xDE59, 0xD83B, 0xDE5B, 0xD83B, 0xDE5D, 0xD83B, 0xDE5F}
+    };
+    static const UChar visualOrder[][MAXLEN]={
+        /* Arabic mathematical Symbols 0x1EE00 - 0x1EE1B */
+        {0xD83B, 0xDE1B, 0xD83B, 0xDE1A, 0xD83B, 0xDE19, 0x20,    
+        0xD83B, 0xDE18, 0xD83B, 0xDE17, 0xD83B, 0xDE16, 0x20,    
+        0xD83B, 0xDE15, 0xD83B, 0xDE14, 0xD83B, 0xDE13, 0xD83B, 0xDE12, 0x20,
+        0xD83B, 0xDE11, 0xD83B, 0xDE10, 0xD83B, 0xDE0F, 0xD83B, 0xDE0E, 0x20,
+        0xD83B, 0xDE0D, 0xD83B, 0xDE0C, 0xD83B, 0xDE0B, 0xD83B, 0xDE0A, 0x20,
+        0xD83B, 0xDE09, 0xD83B, 0xDE08, 0xD83B, 0xDE07, 0x20,    
+        0xD83B, 0xDE06, 0xD83B, 0xDE05, 0xD83B, 0xDE24, 0x20,    
+        0xD83B, 0xDE03, 0xD83B, 0xDE02, 0xD83B, 0xDE01, 0xD83B, 0xDE00},
+        /* Arabic mathematical Symbols - Looped Symbols, 0x1EE80 - 0x1EE9B */
+        {0xD83B, 0xDE9B, 0xD83B, 0xDE9A, 0xD83B, 0xDE99, 0x20,    
+        0xD83B, 0xDE98, 0xD83B, 0xDE97, 0xD83B, 0xDE96, 0x20,    
+        0xD83B, 0xDE95, 0xD83B, 0xDE94, 0xD83B, 0xDE93, 0xD83B, 0xDE92, 0x20,
+        0xD83B, 0xDE91, 0xD83B, 0xDE90, 0xD83B, 0xDE8F, 0xD83B, 0xDE8E, 0x20,
+        0xD83B, 0xDE8D, 0xD83B, 0xDE8C, 0xD83B, 0xDE8B, 0x20,    
+        0xD83B, 0xDE89, 0xD83B, 0xDE88, 0xD83B, 0xDE87, 0x20,    
+        0xD83B, 0xDE86, 0xD83B, 0xDE85, 0xD83B, 0xDE84, 0x20,    
+        0xD83B, 0xDE83, 0xD83B, 0xDE82, 0xD83B, 0xDE81, 0xD83B, 0xDE80},
+        /* Arabic mathematical Symbols - Double-struck Symbols, 0x1EEA1 - 0x1EEBB */
+        {0xD83B, 0xDEBB, 0xD83B, 0xDEBA, 0xD83B, 0xDEB9, 0x20,    
+        0xD83B, 0xDEB8, 0xD83B, 0xDEB7, 0xD83B, 0xDEB6, 0x20,    
+        0xD83B, 0xDEB5, 0xD83B, 0xDEB4, 0xD83B, 0xDEB3, 0xD83B, 0xDEB2, 0x20,
+        0xD83B, 0xDEB1, 0xD83B, 0xDEB0, 0xD83B, 0xDEAF, 0xD83B, 0xDEAE, 0x20,
+        0xD83B, 0xDEAD, 0xD83B, 0xDEAC, 0xD83B, 0xDEAB, 0x20,    
+        0xD83B, 0xDEA9, 0xD83B, 0xDEA8, 0xD83B, 0xDEA7, 0x20,    
+        0xD83B, 0xDEA6, 0xD83B, 0xDEA5, 0x20,            
+        0xD83B, 0xDEA3, 0xD83B, 0xDEA2, 0xD83B, 0xDEA1},
+        /* Arabic mathematical Symbols - Initial Symbols, 0x1EE21 - 0x1EE3B */
+        {0xD83B, 0xDE3B, 0xD83B, 0xDE39, 0x20,            
+        0xD83B, 0xDE37, 0xD83B, 0xDE36, 0x20,            
+        0xD83B, 0xDE35, 0xD83B, 0xDE34, 0xD83B, 0xDE32, 0x20,    
+        0xD83B, 0xDE31, 0xD83B, 0xDE30, 0xD83B, 0xDE2F, 0xD83B, 0xDE2E, 0x20,
+        0xD83B, 0xDE2D, 0xD83B, 0xDE2C, 0xD83B, 0xDE2B, 0xD83B, 0xDE2A, 0x20,
+        0xD83B, 0xDE29, 0xD83B, 0xDE27, 0x20,            
+        0xD83B, 0xDE22, 0xD83B, 0xDE21},
+        /* Arabic mathematical Symbols - Tailed Symbols */
+        {0xD83B, 0xDE5F, 0xD83B, 0xDE5D, 0xD83B, 0xDE5B, 0xD83B, 0xDE59, 0x20,
+        0xD83B, 0xDE57, 0xD83B, 0xDE54, 0xD83B, 0xDE52, 0xD83B, 0xDE51, 0x20,
+        0xD83B, 0xDE4F, 0xD83B, 0xDE4E, 0xD83B, 0xDE4D, 0x20,    
+        0xD83B, 0xDE4B, 0xD83B, 0xDE49, 0xD83B, 0xDE47, 0xD83B, 0xDE42}
+    };
+    char formatChars[MAXLEN];
+    UErrorCode ec = U_ZERO_ERROR;
+    UBiDi* bidi = ubidi_open();
+    int i;
+
+    log_verbose("\nEntering TestReorderArabicMathSymbols\n\n");
+
+    for(i=0;i<LENGTHOF(logicalOrder);i++){
+        int32_t srcSize = u_strlen(logicalOrder[i]);
+        int32_t destSize = srcSize*2;
+        UChar dest[MAXLEN];
+        log_verbose("Testing L2V #1 for case %d\n", i);
+        ec = U_ZERO_ERROR;
+        ubidi_setPara(bidi,logicalOrder[i],srcSize,UBIDI_DEFAULT_LTR ,NULL,&ec);
+        if(U_FAILURE(ec)){
+            log_err("ubidi_setPara(tests[%d], paraLevel %d) failed with errorCode %s\n",
+                    i, UBIDI_DEFAULT_LTR, u_errorName(ec));
+        }
+        /* try pre-flighting */
+        destSize = ubidi_writeReordered(bidi,dest,0,UBIDI_DO_MIRRORING,&ec);
+        if(ec!=U_BUFFER_OVERFLOW_ERROR){
+            log_err("Pre-flighting did not give expected error: Expected: U_BUFFER_OVERFLOW_ERROR. Got: %s \n",u_errorName(ec));
+        }else if(destSize!=srcSize){
+            log_err("Pre-flighting did not give expected size: Expected: %d. Got: %d \n",srcSize,destSize);
+        }else{
+            ec= U_ZERO_ERROR;
+        }
+        destSize=ubidi_writeReordered(bidi,dest,destSize+1,UBIDI_DO_MIRRORING,&ec);
+        if(destSize!=srcSize){
+            log_err("ubidi_writeReordered() destSize and srcSize do not match\n");
+        }else if(memcmp(dest, visualOrder[i], destSize*U_SIZEOF_UCHAR)!=0){
+            log_err("ubidi_writeReordered() did not give expected results for UBIDI_DO_MIRRORING.\n"
+                    "Input   : %s\nExpected: %s\nGot     : %s\nLevels  : %s\nAt Index: %d\n",
+                    logicalOrder[i],visualOrder[i],dest,formatLevels(bidi, formatChars),i);
+        }
+    }
+
+    ubidi_close(bidi);
+
+    log_verbose("\nExiting TestReorderArabicMathSymbols\n\n");
+}
+
+static void
+doTest(UBiDi *pBiDi, int testNumber, const BiDiTestData *test, int32_t lineStart, UBool countRunsFirst) {
     const uint8_t *dirProps=test->text+lineStart;
     const UBiDiLevel *levels=test->levels;
     const uint8_t *visualMap=test->visualMap;
@@ -646,7 +1044,7 @@ doTest(UBiDi *pBiDi, int testNumber, BiDiTestData *test, int32_t lineStart, UBoo
         log_verbose("Calling ubidi_getLogicalMap() first.\n");
     }
 
-    testReordering(pBiDi, testNumber);
+    _testReordering(pBiDi, testNumber);
 
     for(i=0; i<len; ++i) {
         log_verbose("%3d %3d %.*s%-3s @%d\n",
@@ -711,15 +1109,21 @@ doTest(UBiDi *pBiDi, int testNumber, BiDiTestData *test, int32_t lineStart, UBoo
         level=ubidi_getLevelAt(pBiDi, logicalIndex);
         ubidi_getLogicalRun(pBiDi, logicalIndex, &logicalIndex, &level2);
         if(level!=level2) {
-            log_err("ubidi_getLogicalRun(tests[%d], run ending at index %d): wrong level %d\n", testNumber, logicalIndex, level2);
+            log_err("ubidi_getLogicalRun(tests[%d], run ending at index %d): "
+                    "wrong level %d instead of %d\n",
+                    testNumber, logicalIndex, level, level2);
         }
         if(--runCount<0) {
-            log_err("\nubidi_getLogicalRun(tests[%d]): wrong number of runs compared to %d=ubidi_getRunCount()\n", testNumber, ubidi_countRuns(pBiDi, &errorCode));
+            log_err("\nubidi_getLogicalRun(tests[%d]): wrong number of runs "
+                    "compared to %d=ubidi_countRuns()\n",
+                    testNumber, ubidi_countRuns(pBiDi, &errorCode));
             return;
         }
     }
     if(runCount!=0) {
-        log_err("\nubidi_getLogicalRun(tests[%d]): wrong number of runs compared to %d=ubidi_getRunCount()\n", testNumber, ubidi_countRuns(pBiDi, &errorCode));
+        log_err("\nubidi_getLogicalRun(tests[%d]): wrong number of runs "
+                "compared to %d=ubidi_getRunCount()\n",
+                testNumber, ubidi_countRuns(pBiDi, &errorCode));
         return;
     }
 
@@ -727,7 +1131,7 @@ doTest(UBiDi *pBiDi, int testNumber, BiDiTestData *test, int32_t lineStart, UBoo
 }
 
 static void
-testReordering(UBiDi *pBiDi, int testNumber) {
+_testReordering(UBiDi *pBiDi, int testNumber) {
     int32_t
         logicalMap1[MAXLEN], logicalMap2[MAXLEN], logicalMap3[MAXLEN],
         visualMap1[MAXLEN], visualMap2[MAXLEN], visualMap3[MAXLEN], visualMap4[MAXLEN];
@@ -869,27 +1273,519 @@ testReordering(UBiDi *pBiDi, int testNumber) {
     }
 }
 
-static void TestFailureRecovery(void) {
-    UErrorCode status;
+#define RETURN_IF_BAD_ERRCODE(x)    \
+    if (U_FAILURE(errorCode)) {      \
+        log_err("\nbad errorCode %d at %s\n", errorCode, (x));  \
+        return;     \
+    }               \
+
+#define STRING_TEST_CASE(s) { (s), LENGTHOF(s) }
+
+static void testGetBaseDirection(void) {
+    UBiDiDirection dir;
+    int i;
+
+/* Test Data */
+    static const UChar
+/*Mixed Start with L*/
+    stringMixedEnglishFirst[]={ 0x61, 0x627, 0x32, 0x6f3, 0x61, 0x34, 0 },
+/*Mixed Start with AL*/
+    stringMixedArabicFirst[]={ 0x661, 0x627, 0x662, 0x6f3, 0x61, 0x664, 0 },
+/*Mixed Start with R*/
+    stringMixedHebrewFirst[]={ 0x05EA, 0x627, 0x662, 0x6f3, 0x61, 0x664, 0 },
+/*All AL (Arabic. Persian)*/
+    stringPersian[]={0x0698, 0x067E, 0x0686, 0x06AF, 0},
+/*All R (Hebrew etc.)*/
+    stringHebrew[]={0x0590, 0x05D5, 0x05EA, 0x05F1, 0},
+/*All L (English)*/
+    stringEnglish[]={0x71, 0x61, 0x66, 0},
+/*Mixed Start with weak AL an then L*/
+    stringStartWeakAL[]={ 0x0663, 0x71, 0x61, 0x66, 0},
+/*Mixed Start with weak L and then AL*/
+    stringStartWeakL[]={0x31, 0x0698, 0x067E, 0x0686, 0x06AF, 0},
+/*Empty*/
+    stringEmpty[]={0},
+/*Surrogate Char.*/
+    stringSurrogateChar[]={0xD800, 0xDC00, 0},
+/*Invalid UChar*/
+    stringInvalidUchar[]={-1},
+/*All weak L (English Digits)*/
+    stringAllEnglishDigits[]={0x31, 0x32, 0x33, 0},
+/*All weak AL (Arabic Digits)*/
+    stringAllArabicDigits[]={0x0663, 0x0664, 0x0665, 0},
+/*First L (English) others are R (Hebrew etc.) */
+    stringFirstL[] = {0x71, 0x0590, 0x05D5, 0x05EA, 0x05F1, 0},
+/*Last R (Hebrew etc.) others are weak L (English Digits)*/
+    stringLastR[] = {0x31, 0x32, 0x33, 0x05F1, 0};
+
+    static const struct {
+        const UChar *s;
+        int32_t length;
+    } testCases[]={
+        STRING_TEST_CASE(stringMixedEnglishFirst),
+        STRING_TEST_CASE(stringMixedArabicFirst),
+        STRING_TEST_CASE(stringMixedHebrewFirst),
+        STRING_TEST_CASE(stringPersian),
+        STRING_TEST_CASE(stringHebrew),
+        STRING_TEST_CASE(stringEnglish),
+        STRING_TEST_CASE(stringStartWeakAL),
+        STRING_TEST_CASE(stringStartWeakL),
+        STRING_TEST_CASE(stringEmpty),
+        STRING_TEST_CASE(stringSurrogateChar),
+        STRING_TEST_CASE(stringInvalidUchar),
+        STRING_TEST_CASE(stringAllEnglishDigits),
+        STRING_TEST_CASE(stringAllArabicDigits),
+        STRING_TEST_CASE(stringFirstL),
+        STRING_TEST_CASE(stringLastR),
+    };
+
+/* Expected results */
+    static const UBiDiDirection expectedDir[] ={
+        UBIDI_LTR, UBIDI_RTL, UBIDI_RTL,
+        UBIDI_RTL, UBIDI_RTL, UBIDI_LTR,
+        UBIDI_LTR, UBIDI_RTL, UBIDI_NEUTRAL,
+        UBIDI_LTR, UBIDI_NEUTRAL, UBIDI_NEUTRAL,
+        UBIDI_NEUTRAL, UBIDI_LTR, UBIDI_RTL
+    };
+
+    log_verbose("testGetBaseDirection() with %u test cases ---\n",
+    LENGTHOF(testCases));
+/* Run Tests */
+     for(i=0; i<LENGTHOF(testCases); ++i) {
+        dir = ubidi_getBaseDirection(testCases[i].s, testCases[i].length );
+        log_verbose("Testing case %d\tReceived dir %d\n", i, dir);
+        if (dir != expectedDir[i])
+            log_err("\nFailed getBaseDirection case %d Expected  %d \tReceived %d\n",
+            i, expectedDir[i], dir);
+    }
+
+/* Misc. tests */
+/* NULL string */
+    dir = ubidi_getBaseDirection(NULL, 3);
+    if (dir != UBIDI_NEUTRAL )
+        log_err("\nFailed getBaseDirection for NULL string " ,
+        "\nExpected  %d \nReceived %d", UBIDI_NEUTRAL, dir);
+/*All L- English string and length=-3 */
+    dir = ubidi_getBaseDirection( stringEnglish, -3);
+    if (dir != UBIDI_NEUTRAL )
+        log_err("\nFailed getBaseDirection for string w length= -3 ",
+        "\nExpected  %d \nReceived %d", UBIDI_NEUTRAL, dir);
+/*All L- English string and length=-1 */
+    dir = ubidi_getBaseDirection( stringEnglish, -1);
+    if (dir != UBIDI_LTR )
+        log_err("\nFailed getBaseDirection for English string w length= -1 ",
+        "\nExpected  %d \nReceived %d", UBIDI_LTR, dir);
+/*All AL- Persian string and length=-1 */
+    dir = ubidi_getBaseDirection( stringPersian, -1);
+    if (dir != UBIDI_RTL )
+        log_err("\nFailed getBaseDirection for Persian string w length= -1 ",
+        "\nExpected  %d \nReceived %d", UBIDI_RTL, dir);
+/*All R- Hebrew string and length=-1 */
+    dir = ubidi_getBaseDirection( stringHebrew, -1);
+    if (dir != UBIDI_RTL )
+        log_err("\nFailed getBaseDirection for Hebrew string w length= -1 ",
+        "\nExpected  %d \nReceived %d", UBIDI_RTL, dir);
+/*All weak L- English digits string and length=-1 */
+    dir = ubidi_getBaseDirection(stringAllEnglishDigits, -1);
+    if (dir != UBIDI_NEUTRAL )
+        log_err("\nFailed getBaseDirection for English digits string w length= -1 ",
+        "\nExpected  %d \nReceived %d", UBIDI_NEUTRAL, dir);
+/*All weak AL- Arabic digits string and length=-1 */
+    dir = ubidi_getBaseDirection(stringAllArabicDigits, -1);
+    if (dir != UBIDI_NEUTRAL )
+        log_err("\nFailed getBaseDirection for Arabic string w length= -1 ",
+        "\nExpected  %d \nReceived %d", UBIDI_NEUTRAL, dir);
+
+}
+
+
+static void doMisc(void) {
+/* Miscellaneous tests to exercize less popular code paths */
+    UBiDi *bidi, *bidiLine;
+    UChar src[MAXLEN], dest[MAXLEN];
+    int32_t srcLen, destLen, runCount, i;
+    UBiDiLevel level;
+    UBiDiDirection dir;
+    int32_t map[MAXLEN];
+    UErrorCode errorCode=U_ZERO_ERROR;
+    static const int32_t srcMap[6] = {0,1,-1,5,4};
+    static const int32_t dstMap[6] = {0,1,-1,-1,4,3};
+
+    bidi = ubidi_openSized(120, 66, &errorCode);
+    if (bidi == NULL) {
+        log_err("Error with openSized(120, 66)\n");
+        return;
+    }
+    bidiLine = ubidi_open();
+    if (bidi == NULL) {
+        log_err("Error with open()\n");
+        return;
+    }
+
+    destLen = ubidi_writeReverse(src, 0, dest, MAXLEN, 0, &errorCode);
+    if (destLen != 0) {
+        log_err("\nwriteReverse should return zero length, ",
+                "returned %d instead\n", destLen);
+    }
+    RETURN_IF_BAD_ERRCODE("#1#");
+
+    ubidi_setPara(bidi, src, 0, UBIDI_LTR, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN, 0, &errorCode);
+    if (destLen != 0) {
+        log_err("\nwriteReordered should return zero length, ",
+                "returned %d instead\n", destLen);
+    }
+    RETURN_IF_BAD_ERRCODE("#2#");
+
+    srcLen = u_unescape("abc       ", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_RTL, NULL, &errorCode);
+    ubidi_setLine(bidi, 0, 6, bidiLine, &errorCode);
+    for (i = 3; i < 6; i++) {
+        level = ubidi_getLevelAt(bidiLine, i);
+        if (level != UBIDI_RTL) {
+            log_err("\nTrailing space at index %d should get paragraph level"
+                    "%d, got %d instead\n", i, UBIDI_RTL, level);
+        }
+    }
+    RETURN_IF_BAD_ERRCODE("#3#");
+
+    srcLen = u_unescape("abc       def", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_RTL, NULL, &errorCode);
+    ubidi_setLine(bidi, 0, 6, bidiLine, &errorCode);
+    for (i = 3; i < 6; i++) {
+        level = ubidi_getLevelAt(bidiLine, i);
+        if (level != UBIDI_RTL) {
+            log_err("\nTrailing space at index %d should get paragraph level"
+                    "%d, got %d instead\n", i, UBIDI_RTL, level);
+        }
+    }
+    RETURN_IF_BAD_ERRCODE("#4#");
+
+    srcLen = u_unescape("abcdefghi    ", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_RTL, NULL, &errorCode);
+    ubidi_setLine(bidi, 0, 6, bidiLine, &errorCode);
+    for (i = 3; i < 6; i++) {
+        level = ubidi_getLevelAt(bidiLine, i);
+        if (level != 2) {
+            log_err("\nTrailing char at index %d should get level 2, "
+                    "got %d instead\n", i, level);
+        }
+    }
+    RETURN_IF_BAD_ERRCODE("#5#");
+
+    ubidi_setReorderingOptions(bidi, UBIDI_OPTION_REMOVE_CONTROLS);
+    srcLen = u_unescape("\\u200eabc       def", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_RTL, NULL, &errorCode);
+    ubidi_setLine(bidi, 0, 6, bidiLine, &errorCode);
+    destLen = ubidi_getResultLength(bidiLine);
+    if (destLen != 5) {
+        log_err("\nWrong result length, should be 5, got %d\n", destLen);
+    }
+    RETURN_IF_BAD_ERRCODE("#6#");
+
+    srcLen = u_unescape("abcdefghi", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_LTR, NULL, &errorCode);
+    ubidi_setLine(bidi, 0, 6, bidiLine, &errorCode);
+    dir = ubidi_getDirection(bidiLine);
+    if (dir != UBIDI_LTR) {
+        log_err("\nWrong direction #1, should be %d, got %d\n",
+                UBIDI_LTR, dir);
+    }
+    RETURN_IF_BAD_ERRCODE("#7#");
+
+    ubidi_setPara(bidi, src, 0, UBIDI_LTR, NULL, &errorCode);
+    runCount = ubidi_countRuns(bidi, &errorCode);
+    if (runCount != 0) {
+        log_err("\nWrong number of runs #1, should be 0, got %d\n", runCount);
+    }
+    RETURN_IF_BAD_ERRCODE("#8#");
+
+    srcLen = u_unescape("          ", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_RTL, NULL, &errorCode);
+    ubidi_setLine(bidi, 0, 6, bidiLine, &errorCode);
+    runCount = ubidi_countRuns(bidiLine, &errorCode);
+    if (runCount != 1) {
+        log_err("\nWrong number of runs #2, should be 1, got %d\n", runCount);
+    }
+    RETURN_IF_BAD_ERRCODE("#9#");
+
+    srcLen = u_unescape("a\\u05d0        bc", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_RTL, NULL, &errorCode);
+    ubidi_setLine(bidi, 0, 6, bidiLine, &errorCode);
+    dir = ubidi_getDirection(bidi);
+    if (dir != UBIDI_MIXED) {
+        log_err("\nWrong direction #2, should be %d, got %d\n",
+                UBIDI_MIXED, dir);
+    }
+    dir = ubidi_getDirection(bidiLine);
+    if (dir != UBIDI_MIXED) {
+        log_err("\nWrong direction #3, should be %d, got %d\n",
+                UBIDI_MIXED, dir);
+    }
+    runCount = ubidi_countRuns(bidiLine, &errorCode);
+    if (runCount != 2) {
+        log_err("\nWrong number of runs #3, should be 2, got %d\n", runCount);
+    }
+    RETURN_IF_BAD_ERRCODE("#10#");
+
+    ubidi_invertMap(srcMap, map, 5);
+    if (memcmp(dstMap, map, sizeof(dstMap))) {
+        log_err("\nUnexpected inverted Map, got ");
+        for (i = 0; i < 6; i++) {
+            log_err("%d ", map[i]);
+        }
+        log_err("\n");
+    }
+
+    /* test REMOVE_BIDI_CONTROLS together with DO_MIRRORING */
+    srcLen = u_unescape("abc\\u200e", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_LTR, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN,
+              UBIDI_REMOVE_BIDI_CONTROLS | UBIDI_DO_MIRRORING, &errorCode);
+    if (destLen != 3 || memcmp(dest, src, 3 * sizeof(UChar))) {
+        log_err("\nWrong result #1, should be 'abc', got '%s'\n",
+                aescstrdup(dest, destLen));
+    }
+    RETURN_IF_BAD_ERRCODE("#11#");
+
+    /* test inverse Bidi with marks and contextual orientation */
+    ubidi_setReorderingMode(bidi, UBIDI_REORDER_INVERSE_LIKE_DIRECT);
+    ubidi_setReorderingOptions(bidi, UBIDI_OPTION_INSERT_MARKS);
+    ubidi_setPara(bidi, src, 0, UBIDI_DEFAULT_RTL, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN, 0, &errorCode);
+    if (destLen != 0) {
+        log_err("\nWrong result #2, length should be 0, got %d\n", destLen);
+    }
+    RETURN_IF_BAD_ERRCODE("#12#");
+    srcLen = u_unescape("   ", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_DEFAULT_RTL, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN, 0, &errorCode);
+    if (destLen != 3 || memcmp(dest, src, destLen * sizeof(UChar))) {
+        log_err("\nWrong result #3, should be '   ', got '%s'\n",
+                aescstrdup(dest, destLen));
+    }
+    RETURN_IF_BAD_ERRCODE("#13#");
+    srcLen = u_unescape("abc", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_DEFAULT_RTL, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN, 0, &errorCode);
+    if (destLen != 3 || memcmp(dest, src, destLen * sizeof(UChar))) {
+        log_err("\nWrong result #4, should be 'abc', got '%s'\n",
+                aescstrdup(dest, destLen));
+    }
+    RETURN_IF_BAD_ERRCODE("#14#");
+    srcLen = u_unescape("\\u05d0\\u05d1", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_DEFAULT_RTL, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN, 0, &errorCode);
+    srcLen = u_unescape("\\u05d1\\u05d0", src, MAXLEN);
+    if (destLen != 2 || memcmp(dest, src, destLen * sizeof(UChar))) {
+        log_err("\nWrong result #5, should be '%s', got '%s'\n",
+                aescstrdup(src, srcLen), aescstrdup(dest, destLen));
+    }
+    RETURN_IF_BAD_ERRCODE("#15#");
+    srcLen = u_unescape("abc \\u05d0\\u05d1", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_DEFAULT_RTL, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN, 0, &errorCode);
+    srcLen = u_unescape("\\u05d1\\u05d0 abc", src, MAXLEN);
+    if (destLen != 6 || memcmp(dest, src, destLen * sizeof(UChar))) {
+        log_err("\nWrong result #6, should be '%s', got '%s'\n",
+                aescstrdup(src, srcLen), aescstrdup(dest, destLen));
+    }
+    RETURN_IF_BAD_ERRCODE("#16#");
+    srcLen = u_unescape("\\u05d0\\u05d1 abc", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_DEFAULT_RTL, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN, 0, &errorCode);
+    srcLen = u_unescape("\\u200fabc \\u05d1\\u05d0", src, MAXLEN);
+    if (destLen != 7 || memcmp(dest, src, destLen * sizeof(UChar))) {
+        log_err("\nWrong result #7, should be '%s', got '%s'\n",
+                aescstrdup(src, srcLen), aescstrdup(dest, destLen));
+    }
+    RETURN_IF_BAD_ERRCODE("#17#");
+    srcLen = u_unescape("\\u05d0\\u05d1 abc .-=", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_DEFAULT_RTL, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN, 0, &errorCode);
+    srcLen = u_unescape("\\u200f=-. abc \\u05d1\\u05d0", src, MAXLEN);
+    if (destLen != 11 || memcmp(dest, src, destLen * sizeof(UChar))) {
+        log_err("\nWrong result #8, should be '%s', got '%s'\n",
+                aescstrdup(src, srcLen), aescstrdup(dest, destLen));
+    }
+    RETURN_IF_BAD_ERRCODE("#18#");
+    ubidi_orderParagraphsLTR(bidi, TRUE);
+    srcLen = u_unescape("\n\r   \n\rabc\n\\u05d0\\u05d1\rabc \\u05d2\\u05d3\n\r"
+                        "\\u05d4\\u05d5 abc\n\\u05d6\\u05d7 abc .-=\r\n"
+                        "-* \\u05d8\\u05d9 abc .-=", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_DEFAULT_RTL, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN, 0, &errorCode);
+    srcLen = u_unescape("\n\r   \n\rabc\n\\u05d1\\u05d0\r\\u05d3\\u05d2 abc\n\r"
+                        "\\u200fabc \\u05d5\\u05d4\n\\u200f=-. abc \\u05d7\\u05d6\r\n"
+                        "\\u200f=-. abc \\u05d9\\u05d8 *-", src, MAXLEN);
+    if (destLen != 57 || memcmp(dest, src, destLen * sizeof(UChar))) {
+        log_err("\nWrong result #9, should be '%s', got '%s'\n",
+                aescstrdup(src, srcLen), aescstrdup(dest, destLen));
+    }
+    RETURN_IF_BAD_ERRCODE("#19#");
+    srcLen = u_unescape("\\u05d0 \t", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_LTR, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN, 0, &errorCode);
+    srcLen = u_unescape("\\u05D0\\u200e \t", src, MAXLEN);
+    if (destLen != 4 || memcmp(dest, src, destLen * sizeof(UChar))) {
+        log_err("\nWrong result #10, should be '%s', got '%s'\n",
+                aescstrdup(src, srcLen), aescstrdup(dest, destLen));
+    }
+    RETURN_IF_BAD_ERRCODE("#20#");
+    srcLen = u_unescape("\\u05d0 123 \t\\u05d1 123 \\u05d2", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_LTR, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN, 0, &errorCode);
+    srcLen = u_unescape("\\u05d0 \\u200e123\\u200e \t\\u05d2 123 \\u05d1", src, MAXLEN);
+    if (destLen != 16 || memcmp(dest, src, destLen * sizeof(UChar))) {
+        log_err("\nWrong result #11, should be '%s', got '%s'\n",
+                aescstrdup(src, srcLen), aescstrdup(dest, destLen));
+    }
+    RETURN_IF_BAD_ERRCODE("#21#");
+    srcLen = u_unescape("\\u05d0 123 \\u0660\\u0661 ab", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_LTR, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN, 0, &errorCode);
+    srcLen = u_unescape("\\u05d0 \\u200e123 \\u200e\\u0660\\u0661 ab", src, MAXLEN);
+    if (destLen != 13 || memcmp(dest, src, destLen * sizeof(UChar))) {
+        log_err("\nWrong result #12, should be '%s', got '%s'\n",
+                aescstrdup(src, srcLen), aescstrdup(dest, destLen));
+    }
+    RETURN_IF_BAD_ERRCODE("#22#");
+    srcLen = u_unescape("ab \t", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_RTL, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN, 0, &errorCode);
+    srcLen = u_unescape("\\u200f\t ab", src, MAXLEN);
+    if (destLen != 5 || memcmp(dest, src, destLen * sizeof(UChar))) {
+        log_err("\nWrong result #13, should be '%s', got '%s'\n",
+                aescstrdup(src, srcLen), aescstrdup(dest, destLen));
+    }
+    RETURN_IF_BAD_ERRCODE("#23#");
+
+    /* check exceeding para level */
+    ubidi_close(bidi);
+    bidi = ubidi_open();
+    srcLen = u_unescape("A\\u202a\\u05d0\\u202aC\\u202c\\u05d1\\u202cE", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_MAX_EXPLICIT_LEVEL - 1, NULL, &errorCode);
+    level = ubidi_getLevelAt(bidi, 2);
+    if (level != 61) {
+        log_err("\nWrong level at index 2\n, should be 61, got %d\n", level);
+    }
+    RETURN_IF_BAD_ERRCODE("#24#");
+
+    /* check 1-char runs with RUNS_ONLY */
+    ubidi_setReorderingMode(bidi, UBIDI_REORDER_RUNS_ONLY);
+    srcLen = u_unescape("a \\u05d0 b \\u05d1 c \\u05d2 d ", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_LTR, NULL, &errorCode);
+    runCount = ubidi_countRuns(bidi, &errorCode);
+    if (runCount != 14) {
+        log_err("\nWrong number of runs #3, should be 14, got %d\n", runCount);
+    }
+    RETURN_IF_BAD_ERRCODE("#25#");
+
+    ubidi_close(bidi);
+    ubidi_close(bidiLine);
+}
+
+static void
+testFailureRecovery(void) {
+    UErrorCode errorCode;
+    UBiDi *bidi, *bidiLine;
+    UChar src[MAXLEN];
+    int32_t srcLen;
+    UBiDiLevel level;
+    UBiDiReorderingMode rm;
+    static UBiDiLevel myLevels[3] = {6,5,4};
 
-    status = U_FILE_ACCESS_ERROR;
-    if (ubidi_writeReordered(NULL, NULL, 0, 0, &status) != 0) {
+    log_verbose("\nEntering TestFailureRecovery\n\n");
+    errorCode = U_FILE_ACCESS_ERROR;
+    if (ubidi_writeReordered(NULL, NULL, 0, 0, &errorCode) != 0) {
         log_err("ubidi_writeReordered did not return 0 when passed a failing UErrorCode\n");
     }
-    if (ubidi_writeReverse(NULL, 0, NULL, 0, 0, &status) != 0) {
+    if (ubidi_writeReverse(NULL, 0, NULL, 0, 0, &errorCode) != 0) {
         log_err("ubidi_writeReverse did not return 0 when passed a failing UErrorCode\n");
     }
-    status = U_ZERO_ERROR;
-    if (ubidi_writeReordered(NULL, NULL, 0, 0, &status) != 0 || status != U_ILLEGAL_ARGUMENT_ERROR) {
+    errorCode = U_ZERO_ERROR;
+    if (ubidi_writeReordered(NULL, NULL, 0, 0, &errorCode) != 0 || errorCode != U_ILLEGAL_ARGUMENT_ERROR) {
         log_err("ubidi_writeReordered did not fail as expected\n");
     }
-    status = U_ZERO_ERROR;
-    if (ubidi_writeReverse(NULL, 0, NULL, 0, 0, &status) != 0 || status != U_ILLEGAL_ARGUMENT_ERROR) {
+
+    bidi = ubidi_open();
+    srcLen = u_unescape("abc", src, MAXLEN);
+    errorCode = U_ZERO_ERROR;
+    ubidi_setPara(bidi, src, srcLen, UBIDI_DEFAULT_LTR - 1, NULL, &errorCode);
+    if (U_SUCCESS(errorCode)) {
+        log_err("\nubidi_setPara did not fail when passed too big para level\n");
+    }
+    errorCode = U_ZERO_ERROR;
+    if (ubidi_writeReverse(NULL, 0, NULL, 0, 0, &errorCode) != 0 || errorCode != U_ILLEGAL_ARGUMENT_ERROR) {
         log_err("ubidi_writeReverse did not fail as expected\n");
     }
+    bidiLine = ubidi_open();
+    errorCode = U_ZERO_ERROR;
+    ubidi_setLine(bidi, 0, 6, bidiLine, &errorCode);
+    if (U_SUCCESS(errorCode)) {
+        log_err("\nubidi_setLine did not fail when called before valid setPara()\n");
+    }
+    errorCode = U_ZERO_ERROR;
+    srcLen = u_unescape("abc", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_LTR + 4, NULL, &errorCode);
+    level = ubidi_getLevelAt(bidi, 3);
+    if (level != 0) {
+        log_err("\nubidi_getLevelAt did not fail when called with bad argument\n");
+    }
+    errorCode = U_ZERO_ERROR;
+    ubidi_close(bidi);
+    bidi = ubidi_openSized(-1, 0, &errorCode);
+    if (U_SUCCESS(errorCode)) {
+        log_err("\nubidi_openSized did not fail when called with bad argument\n");
+    }
+    ubidi_close(bidi);
+    bidi = ubidi_openSized(2, 1, &errorCode);
+    errorCode = U_ZERO_ERROR;
+    srcLen = u_unescape("abc", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_LTR, NULL, &errorCode);
+    if (U_SUCCESS(errorCode)) {
+        log_err("\nsetPara did not fail when called with text too long\n");
+    }
+    errorCode = U_ZERO_ERROR;
+    srcLen = u_unescape("=2", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_RTL, NULL, &errorCode);
+    ubidi_countRuns(bidi, &errorCode);
+    if (U_SUCCESS(errorCode)) {
+        log_err("\nsetPara did not fail when called for too many runs\n");
+    }
+    ubidi_close(bidi);
+    bidi = ubidi_open();
+    rm = ubidi_getReorderingMode(bidi);
+    ubidi_setReorderingMode(bidi, UBIDI_REORDER_DEFAULT - 1);
+    if (rm != ubidi_getReorderingMode(bidi)) {
+        log_err("\nsetReorderingMode with bad argument #1 should have no effect\n");
+    }
+    ubidi_setReorderingMode(bidi, 9999);
+    if (rm != ubidi_getReorderingMode(bidi)) {
+        log_err("\nsetReorderingMode with bad argument #2 should have no effect\n");
+    }
+
+    /* Try a surrogate char */
+    errorCode = U_ZERO_ERROR;
+    srcLen = u_unescape("\\uD800\\uDC00", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, UBIDI_RTL, NULL, &errorCode);
+    if (ubidi_getDirection(bidi) != UBIDI_MIXED) {
+        log_err("\ngetDirection for 1st surrogate char should be MIXED\n");
+    }
+    errorCode = U_ZERO_ERROR;
+    srcLen = u_unescape("abc", src, MAXLEN);
+    ubidi_setPara(bidi, src, srcLen, 5, myLevels, &errorCode);
+    if (U_SUCCESS(errorCode)) {
+        log_err("\nsetPara did not fail when called with bad levels\n");
+    }
+    ubidi_close(bidi);
+    ubidi_close(bidiLine);
+
+    log_verbose("\nExiting TestFailureRecovery\n\n");
 }
 
-static void TestMultipleParagraphs(void) {
+static void
+testMultipleParagraphs(void) {
     static const char* const text = "__ABC\\u001c"          /* Para #0 offset 0 */
                                     "__\\u05d0DE\\u001c"    /*       1        6 */
                                     "__123\\u001c"          /*       2       12 */
@@ -912,16 +1808,39 @@ static void TestMultipleParagraphs(void) {
                                                   {23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23}};
     static const char* const text2 = "\\u05d0 1-2\\u001c\\u0630 1-2\\u001c1-2";
     static const UBiDiLevel levels2[] = {1,1,2,2,2,0, 1,1,2,1,2,0, 2,2,2};
+    static UBiDiLevel myLevels[10] = {0,0,0,0,0,0,0,0,0,0};
+    static const UChar multiparaTestString[] = {
+        0x5de, 0x5e0, 0x5e1, 0x5d4, 0x20,  0x5e1, 0x5e4, 0x5da,
+        0x20,  0xa,   0xa,   0x41,  0x72,  0x74,  0x69,  0x73,
+        0x74,  0x3a,  0x20,  0x5de, 0x5e0, 0x5e1, 0x5d4, 0x20,
+        0x5e1, 0x5e4, 0x5da, 0x20,  0xa,   0xa,   0x41,  0x6c,
+        0x62,  0x75,  0x6d,  0x3a,  0x20,  0x5de, 0x5e0, 0x5e1,
+        0x5d4, 0x20,  0x5e1, 0x5e4, 0x5da, 0x20,  0xa,   0xa,
+        0x54,  0x69,  0x6d,  0x65,  0x3a,  0x20,  0x32,  0x3a,
+        0x32,  0x37,  0xa,  0xa
+    };
+    static const UBiDiLevel multiparaTestLevels[] = {
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 1, 1, 1, 1, 1,
+        1, 1, 1, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 1, 1, 1,
+        1, 1, 1, 1, 1, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0
+    };
     UBiDiLevel gotLevel;
     const UBiDiLevel* gotLevels;
     UBool orderParagraphsLTR;
-    UChar src[MAXLEN];
+    UChar src[MAXLEN], dest[MAXLEN];
     UErrorCode errorCode=U_ZERO_ERROR;
     UBiDi* pBidi=ubidi_open();
     UBiDi* pLine;
     int32_t srcSize, count, paraStart, paraLimit, paraIndex, length;
+    int32_t srcLen, destLen;
     int i, j, k;
 
+    log_verbose("\nEntering TestMultipleParagraphs\n\n");
     u_unescape(text, src, MAXLEN);
     srcSize=u_strlen(src);
     ubidi_setPara(pBidi, src, srcSize, UBIDI_LTR, NULL, &errorCode);
@@ -1125,6 +2044,10 @@ static void TestMultipleParagraphs(void) {
     ubidi_orderParagraphsLTR(pBidi, TRUE);
     ubidi_setPara(pBidi, src, srcSize, UBIDI_RTL, NULL, &errorCode);
     gotLevels=ubidi_getLevels(pBidi, &errorCode);
+    if (U_FAILURE(errorCode)) {
+        log_err("Can't get levels. %s\n", u_errorName(errorCode));
+        return;
+    }
     for (i=0; i<srcSize; i++) {
         if (gotLevels[i]!=levels2[i]) {
             log_err("Checking leading numerics: for char %d(%04x), level=%d, expected=%d\n",
@@ -1139,9 +2062,9 @@ static void TestMultipleParagraphs(void) {
     srcSize = 5;
     ubidi_orderParagraphsLTR(pBidi, TRUE);
     for (i=0x001c; i<=0x0020; i+=(0x0020-0x001c)) {
-        src[4]=i;                       /* with and without terminating B */
+        src[4]=(UChar)i;                /* with and without terminating B */
         for (j=0x0041; j<=0x05d0; j+=(0x05d0-0x0041)) {
-            src[0]=j;                   /* leading 'A' or Alef */
+            src[0]=(UChar)j;            /* leading 'A' or Alef */
             for (gotLevel=4; gotLevel<=5; gotLevel++) {
                 /* test even and odd paraLevel */
                 ubidi_setPara(pBidi, src, srcSize, gotLevel, NULL, &errorCode);
@@ -1157,8 +2080,74 @@ static void TestMultipleParagraphs(void) {
         }
     }
 
+    /* check default orientation when inverse bidi and paragraph starts
+     * with LTR strong char and ends with RTL strong char, with and without
+     * a terminating B
+     */
+    ubidi_setReorderingMode(pBidi, UBIDI_REORDER_INVERSE_LIKE_DIRECT);
+    srcLen = u_unescape("abc \\u05d2\\u05d1\n", src, MAXLEN);
+    ubidi_setPara(pBidi, src, srcLen, UBIDI_DEFAULT_LTR, NULL, &errorCode);
+    destLen = ubidi_writeReordered(pBidi, dest, MAXLEN, 0, &errorCode);
+    srcLen = u_unescape("\\u05d1\\u05d2 abc\n", src, MAXLEN);
+    if (memcmp(src, dest, destLen * sizeof(UChar))) {
+        log_err("\nInvalid output #0, should be '%s', got '%s'\n",
+                aescstrdup(src, srcLen), aescstrdup(dest, destLen));
+    }
+    srcLen = u_unescape("abc \\u05d2\\u05d1", src, MAXLEN);
+    ubidi_setPara(pBidi, src, srcLen, UBIDI_DEFAULT_LTR, NULL, &errorCode);
+    destLen = ubidi_writeReordered(pBidi, dest, MAXLEN, 0, &errorCode);
+    srcLen = u_unescape("\\u05d1\\u05d2 abc", src, MAXLEN);
+    if (memcmp(src, dest, destLen * sizeof(UChar))) {
+        log_err("\nInvalid output #1, should be '%s', got '%s'\n",
+                aescstrdup(src, srcLen), aescstrdup(dest, destLen));
+    }
+
+    /* check multiple paragraphs together with explicit levels
+     */
+    ubidi_setReorderingMode(pBidi, UBIDI_REORDER_DEFAULT);
+    srcLen = u_unescape("ab\\u05d1\\u05d2\n\\u05d3\\u05d4123", src, MAXLEN);
+    ubidi_setPara(pBidi, src, srcLen, UBIDI_LTR, myLevels, &errorCode);
+    destLen = ubidi_writeReordered(pBidi, dest, MAXLEN, 0, &errorCode);
+    srcLen = u_unescape("ab\\u05d2\\u05d1\\n123\\u05d4\\u05d3", src, MAXLEN);
+    if (memcmp(src, dest, destLen * sizeof(UChar))) {
+        log_err("\nInvalid output #2, should be '%s', got '%s'\n",
+                aescstrdup(src, srcLen), aescstrdup(dest, destLen));
+    }
+    count = ubidi_countParagraphs(pBidi);
+    if (count != 2) {
+        log_err("\nInvalid number of paras, should be 2, got %d\n", count);
+    }
+
     ubidi_close(pLine);
     ubidi_close(pBidi);
+    log_verbose("\nExiting TestMultipleParagraphs\n\n");
+
+    /* check levels in multiple paragraphs with default para level
+     */
+    pBidi = ubidi_open();
+    errorCode = U_ZERO_ERROR;
+    ubidi_setPara(pBidi, multiparaTestString, LENGTHOF(multiparaTestString),
+                  UBIDI_DEFAULT_LTR, NULL, &errorCode);
+    if (U_FAILURE(errorCode)) {
+        log_err("ubidi_setPara failed for multiparaTestString\n");
+        ubidi_close(pBidi);
+        return;
+    }
+    gotLevels = ubidi_getLevels(pBidi, &errorCode);
+    if (U_FAILURE(errorCode)) {
+        log_err("ubidi_getLevels failed for multiparaTestString\n");
+        ubidi_close(pBidi);
+        return;
+    }
+    for (i = 0; i < LENGTHOF(multiparaTestString); i++) {
+        if (gotLevels[i] != multiparaTestLevels[i]) {
+            log_err("Error on level for multiparaTestString at index %d, "
+                    "expected=%d, actual=%d\n",
+                    i, multiparaTestLevels[i], gotLevels[i]);
+        }
+    }
+    ubidi_close(pBidi);
+
 }
 
 
@@ -1169,7 +2158,7 @@ static int countRoundtrips=0, countNonRoundtrips=0;
 #define STRING_TEST_CASE(s) { (s), LENGTHOF(s) }
 
 static void
-doInverseBiDiTest() {
+testInverse(void) {
     static const UChar
         string0[]={ 0x6c, 0x61, 0x28, 0x74, 0x69, 0x6e, 0x20, 0x5d0, 0x5d1, 0x29, 0x5d2, 0x5d3 },
         string1[]={ 0x6c, 0x61, 0x74, 0x20, 0x5d0, 0x5d1, 0x5d2, 0x20, 0x31, 0x32, 0x33 },
@@ -1192,32 +2181,41 @@ doInverseBiDiTest() {
     UErrorCode errorCode;
     int i;
 
+    log_verbose("\nEntering TestInverse\n\n");
     pBiDi=ubidi_open();
     if(pBiDi==NULL) {
         log_err("unable to open a UBiDi object (out of memory)\n");
         return;
     }
 
-    log_verbose("inverse BiDi: testInverseBiDi(L) with %u test cases ---\n", LENGTHOF(testCases));
+    log_verbose("inverse Bidi: testInverse(L) with %u test cases ---\n", LENGTHOF(testCases));
      for(i=0; i<LENGTHOF(testCases); ++i) {
+        log_verbose("Testing case %d\n", i);
         errorCode=U_ZERO_ERROR;
-        testInverseBiDi(pBiDi, testCases[i].s, testCases[i].length, 0, &errorCode);
+        _testInverseBidi(pBiDi, testCases[i].s, testCases[i].length, 0, &errorCode);
     }
 
-    log_verbose("inverse BiDi: testInverseBiDi(R) with %u test cases ---\n", LENGTHOF(testCases));
+    log_verbose("inverse Bidi: testInverse(R) with %u test cases ---\n", LENGTHOF(testCases));
     for(i=0; i<LENGTHOF(testCases); ++i) {
+        log_verbose("Testing case %d\n", i);
         errorCode=U_ZERO_ERROR;
-        testInverseBiDi(pBiDi, testCases[i].s, testCases[i].length, 1, &errorCode);
+        _testInverseBidi(pBiDi, testCases[i].s, testCases[i].length, 1, &errorCode);
     }
 
-    testManyInverseBiDi(pBiDi, 0);
-    testManyInverseBiDi(pBiDi, 1);
+    _testManyInverseBidi(pBiDi, 0);
+    _testManyInverseBidi(pBiDi, 1);
 
     ubidi_close(pBiDi);
 
-    log_verbose("inverse BiDi: rountrips: %5u\nnon-roundtrips: %5u\n", countRoundtrips, countNonRoundtrips);
+    log_verbose("inverse Bidi: rountrips: %5u\nnon-roundtrips: %5u\n", countRoundtrips, countNonRoundtrips);
+
+    _testWriteReverse();
+
+    _testManyAddedPoints();
 
-    testWriteReverse();
+    _testMisc();
+
+    log_verbose("\nExiting TestInverse\n\n");
 }
 
 #define COUNT_REPEAT_SEGMENTS 6
@@ -1232,12 +2230,13 @@ static const UChar repeatSegments[COUNT_REPEAT_SEGMENTS][2]={
 };
 
 static void
-testManyInverseBiDi(UBiDi *pBiDi, UBiDiLevel direction) {
+_testManyInverseBidi(UBiDi *pBiDi, UBiDiLevel direction) {
     UChar text[8]={ 0, 0, 0x20, 0, 0, 0x20, 0, 0 };
     int i, j, k;
     UErrorCode errorCode;
 
-    log_verbose("inverse BiDi: testManyInverseBiDi(%c) - test permutations of text snippets ---\n", direction==0 ? 'L' : 'R');
+    log_verbose("inverse Bidi: testManyInverseBidi(%c) - test permutations of text snippets ---\n",
+                 direction==0 ? 'L' : 'R');
     for(i=0; i<COUNT_REPEAT_SEGMENTS; ++i) {
         text[0]=repeatSegments[i][0];
         text[1]=repeatSegments[i][1];
@@ -1249,21 +2248,21 @@ testManyInverseBiDi(UBiDi *pBiDi, UBiDiLevel direction) {
                 text[7]=repeatSegments[k][1];
 
                 errorCode=U_ZERO_ERROR;
-                log_verbose("inverse BiDi: testManyInverseBiDi()[%u %u %u]\n", i, j, k);
-                testInverseBiDi(pBiDi, text, 8, direction, &errorCode);
+                log_verbose("inverse Bidi: testManyInverseBidi()[%u %u %u]\n", i, j, k);
+                _testInverseBidi(pBiDi, text, 8, direction, &errorCode);
             }
         }
     }
 }
 
 static void
-testInverseBiDi(UBiDi *pBiDi, const UChar *src, int32_t srcLength,
+_testInverseBidi(UBiDi *pBiDi, const UChar *src, int32_t srcLength,
                 UBiDiLevel direction, UErrorCode *pErrorCode) {
     UChar visualLTR[MAXLEN], logicalDest[MAXLEN], visualDest[MAXLEN];
     int32_t ltrLength, logicalLength, visualLength;
 
     if(direction==0) {
-        log_verbose("inverse BiDi: testInverseBiDi(L)\n");
+        log_verbose("inverse Bidi: testInverse(L)\n");
 
         /* convert visual to logical */
         ubidi_setInverse(pBiDi, TRUE);
@@ -1289,7 +2288,7 @@ testInverseBiDi(UBiDi *pBiDi, const UChar *src, int32_t srcLength,
         visualLength=ubidi_writeReordered(pBiDi, visualDest, LENGTHOF(visualDest),
                                           UBIDI_DO_MIRRORING|UBIDI_REMOVE_BIDI_CONTROLS, pErrorCode);
     } else {
-        log_verbose("inverse BiDi: testInverseBiDi(R)\n");
+        log_verbose("inverse Bidi: testInverse(R)\n");
 
         /* reverse visual from RTL to LTR */
         ltrLength=ubidi_writeReverse(src, srcLength, visualLTR, LENGTHOF(visualLTR), 0, pErrorCode);
@@ -1335,7 +2334,7 @@ testInverseBiDi(UBiDi *pBiDi, const UChar *src, int32_t srcLength,
 }
 
 static void
-testWriteReverse() {
+_testWriteReverse(void) {
     /* U+064e and U+0650 are combining marks (Mn) */
     static const UChar forward[]={
         0x200f, 0x627, 0x64e, 0x650, 0x20, 0x28, 0x31, 0x29
@@ -1372,10 +2371,60 @@ testWriteReverse() {
     }
 }
 
+static void _testManyAddedPoints(void) {
+    UErrorCode errorCode = U_ZERO_ERROR;
+    UBiDi *bidi = ubidi_open();
+    UChar text[90], dest[MAXLEN], expected[120];
+    int destLen, i;
+    for (i = 0; i < LENGTHOF(text); i+=3) {
+        text[i] = 0x0061; /* 'a' */
+        text[i+1] = 0x05d0;
+        text[i+2] = 0x0033; /* '3' */
+    }
+    ubidi_setReorderingMode(bidi, UBIDI_REORDER_INVERSE_LIKE_DIRECT);
+    ubidi_setReorderingOptions(bidi, UBIDI_OPTION_INSERT_MARKS);
+    ubidi_setPara(bidi, text, LENGTHOF(text), UBIDI_LTR, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN, 0, &errorCode);
+    for (i = 0; i < LENGTHOF(expected); i+=4) {
+        expected[i] = 0x0061; /* 'a' */
+        expected[i+1] = 0x05d0;
+        expected[i+2] = 0x200e;
+        expected[i+3] = 0x0033; /* '3' */
+    }
+    if (memcmp(dest, expected, destLen * sizeof(UChar))) {
+        log_err("\nInvalid output with many added points, "
+                "expected '%s', got '%s'\n",
+                aescstrdup(expected, LENGTHOF(expected)),
+                aescstrdup(dest, destLen));
+    }
+    ubidi_close(bidi);
+}
+
+static void _testMisc(void) {
+    UErrorCode errorCode = U_ZERO_ERROR;
+    UBiDi *bidi = ubidi_open();
+    UChar src[3], dest[MAXLEN], expected[5];
+    int destLen;
+    ubidi_setInverse(bidi, TRUE);
+    src[0] = src[1] = src[2] = 0x0020;
+    ubidi_setPara(bidi, src, LENGTHOF(src), UBIDI_RTL, NULL, &errorCode);
+    destLen = ubidi_writeReordered(bidi, dest, MAXLEN,
+              UBIDI_OUTPUT_REVERSE | UBIDI_INSERT_LRM_FOR_NUMERIC,
+              &errorCode);
+    u_unescape("\\u200f   \\u200f", expected, 5);
+    if (memcmp(dest, expected, destLen * sizeof(UChar))) {
+        log_err("\nInvalid output with RLM at both sides, "
+                "expected '%s', got '%s'\n",
+                aescstrdup(expected, LENGTHOF(expected)),
+                aescstrdup(dest, destLen));
+    }
+    ubidi_close(bidi);
+}
+
 /* arabic shaping ----------------------------------------------------------- */
 
 static void
-doArabicShapingTest() {
+doArabicShapingTest(void) {
     static const UChar
     source[]={
         0x31,   /* en:1 */
@@ -1397,6 +2446,8 @@ doArabicShapingTest() {
         0x661, 0x627, 0x32, 0x6f3, 0x61, 0x34, 0
     }, reverse_alen2an_init_al[]={
         0x6f1, 0x627, 0x32, 0x6f3, 0x61, 0x6f4, 0
+    }, lamalef[]={
+        0xfefb, 0
     };
     UChar dest[8];
     UErrorCode errorCode;
@@ -1557,10 +2608,20 @@ doArabicShapingTest() {
     if(errorCode!=U_ILLEGAL_ARGUMENT_ERROR) {
         log_err("failure in u_shapeArabic(U_SHAPE_DIGIT_TYPE_RESERVED), returned %s instead of U_ILLEGAL_ARGUMENT_ERROR\n", u_errorName(errorCode));
     }
+
+    errorCode=U_ZERO_ERROR;
+    length=u_shapeArabic(lamalef, LENGTHOF(lamalef),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_LETTERS_UNSHAPE | U_SHAPE_LENGTH_GROW_SHRINK | U_SHAPE_TEXT_DIRECTION_VISUAL_LTR,
+                         &errorCode);
+    if(U_FAILURE(errorCode) || length == LENGTHOF(lamalef)) {
+        log_err("failure in u_shapeArabic(U_SHAPE_LETTERS_UNSHAPE | U_SHAPE_LENGTH_GROW_SHRINK | U_SHAPE_TEXT_DIRECTION_VISUAL_LTR)\n");
+        log_err("returned %s instead of U_ZERO_ERROR or returned length %d instead of 3\n", u_errorName(errorCode), length);
+    }
 }
 
 static void
-doLamAlefSpecialVLTRArabicShapingTest() {
+doLamAlefSpecialVLTRArabicShapingTest(void) {
     static const UChar
     source[]={
 /*a*/   0x20 ,0x646,0x622,0x644,0x627,0x20,
@@ -1711,7 +2772,7 @@ doLamAlefSpecialVLTRArabicShapingTest() {
 }
 
 static void
-doTashkeelSpecialVLTRArabicShapingTest() {
+doTashkeelSpecialVLTRArabicShapingTest(void) {
     static const UChar
     source[]={
         0x64A,0x628,0x631,0x639,0x20,
@@ -1763,7 +2824,7 @@ doTashkeelSpecialVLTRArabicShapingTest() {
 }
 
 static void
-doLOGICALArabicDeShapingTest() {
+doLOGICALArabicDeShapingTest(void) {
     static const UChar
     source[]={
         0x0020,0x0020,0x0020,0xFE8D,0xFEF5,0x0020,0xFEE5,0x0020,0xFE8D,0xFEF7,0x0020,
@@ -1841,10 +2902,420 @@ doLOGICALArabicDeShapingTest() {
 
 }
 
-/* helpers ------------------------------------------------------------------ */
+static void
+doTailTest(void) {
+  static const UChar src[] = { 0x0020, 0x0633, 0 };
+  static const UChar dst_old[] = { 0xFEB1, 0x200B,0 };
+  static const UChar dst_new[] = { 0xFEB1, 0xFE73,0 };
+  UChar dst[3] = { 0x0000, 0x0000,0 };
+  int32_t length;
+  UErrorCode status;
+  
+  log_verbose("SRC: U+%04X U+%04X\n", src[0],src[1]);
+
+  log_verbose("Trying old tail\n");
+  status = U_ZERO_ERROR;
+  length = u_shapeArabic(src, -1, dst, LENGTHOF(dst),
+                         U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR, &status);
+  if(U_FAILURE(status)) {
+    log_err("Fail: status %s\n", u_errorName(status)); 
+  } else if(length!=2) {
+    log_err("Fail: len %d expected 3\n", length);
+  } else if(u_strncmp(dst,dst_old,LENGTHOF(dst))) {
+    log_err("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
+            dst[0],dst[1],dst_old[0],dst_old[1]);
+  } else {
+    log_verbose("OK:  U+%04X U+%04X len %d err %s\n",
+            dst[0],dst[1],length,u_errorName(status));
+  }
+
+
+  log_verbose("Trying new tail\n");
+  status = U_ZERO_ERROR;
+  length = u_shapeArabic(src, -1, dst, LENGTHOF(dst),
+                         U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR|U_SHAPE_TAIL_NEW_UNICODE, &status);
+  if(U_FAILURE(status)) {
+    log_err("Fail: status %s\n", u_errorName(status)); 
+  } else if(length!=2) {
+    log_err("Fail: len %d expected 3\n", length);
+  } else if(u_strncmp(dst,dst_new,LENGTHOF(dst))) {
+    log_err("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
+            dst[0],dst[1],dst_new[0],dst_new[1]);
+  } else {
+    log_verbose("OK:  U+%04X U+%04X len %d err %s\n",
+            dst[0],dst[1],length,u_errorName(status));
+  }
+}
 
 static void
-initCharFromDirProps() {
+doArabicShapingTestForBug5421(void) {
+    static const UChar
+    persian_letters_source[]={
+        0x0020, 0x0698, 0x067E, 0x0686, 0x06AF, 0x0020
+    }, persian_letters[]={
+        0x0020, 0xFB8B, 0xFB59, 0xFB7D, 0xFB94, 0x0020
+    }, tashkeel_aggregation_source[]={
+        0x0020, 0x0628, 0x0651, 0x064E, 0x062A, 0x0631, 0x0645, 0x0020,
+        0x0628, 0x064E, 0x0651, 0x062A, 0x0631, 0x0645, 0x0020
+    }, tashkeel_aggregation[]={
+        0x0020, 0xFE90, 0xFC60, 0xFE97, 0xFEAE, 0xFEE3,
+        0x0020, 0xFE90, 0xFC60, 0xFE97, 0xFEAE, 0xFEE3, 0x0020
+    }, untouched_presentation_source[]={
+        0x0020 ,0x0627, 0xfe90,0x0020
+    }, untouched_presentation[]={
+        0x0020,0xfe8D, 0xfe90,0x0020
+    }, untouched_presentation_r_source[]={
+        0x0020 ,0xfe90, 0x0627, 0x0020
+    }, untouched_presentation_r[]={
+        0x0020, 0xfe90,0xfe8D,0x0020
+    };
+
+    UChar dest[38];
+    UErrorCode errorCode;
+    int32_t length;
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(persian_letters_source, LENGTHOF(persian_letters_source),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_LETTERS_SHAPE|U_SHAPE_TEXT_DIRECTION_VISUAL_LTR,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(persian_letters) || memcmp(dest, persian_letters, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(persian_letters)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(tashkeel_aggregation_source, LENGTHOF(tashkeel_aggregation_source),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_AGGREGATE_TASHKEEL|U_SHAPE_PRESERVE_PRESENTATION|
+                         U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED|U_SHAPE_TEXT_DIRECTION_VISUAL_LTR,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(tashkeel_aggregation) || memcmp(dest, tashkeel_aggregation, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(tashkeel_aggregation)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(untouched_presentation_source, LENGTHOF(untouched_presentation_source),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_PRESERVE_PRESENTATION|
+                         U_SHAPE_LETTERS_SHAPE|U_SHAPE_TEXT_DIRECTION_VISUAL_LTR,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(untouched_presentation) || memcmp(dest, untouched_presentation, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(untouched_presentation)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(untouched_presentation_r_source, LENGTHOF(untouched_presentation_r_source),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_PRESERVE_PRESENTATION|
+                         U_SHAPE_LETTERS_SHAPE|U_SHAPE_TEXT_DIRECTION_LOGICAL,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(untouched_presentation_r) || memcmp(dest, untouched_presentation_r, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(untouched_presentation_r)\n");
+    }
+}
+
+static void
+doArabicShapingTestForBug8703(void) {
+    static const UChar
+    letters_source1[]={
+        0x0634,0x0651,0x0645,0x0652,0x0633
+    }, letters_source2[]={
+        0x0634,0x0651,0x0645,0x0652,0x0633 
+    }, letters_source3[]={
+       0x0634,0x0651,0x0645,0x0652,0x0633
+    }, letters_source4[]={
+        0x0634,0x0651,0x0645,0x0652,0x0633 
+    }, letters_source5[]={
+        0x0633,0x0652,0x0645,0x0651,0x0634
+    }, letters_source6[]={
+        0x0633,0x0652,0x0645,0x0651,0x0634 
+    }, letters_source7[]={
+        0x0633,0x0652,0x0645,0x0651,0x0634
+    }, letters_source8[]={
+        0x0633,0x0652,0x0645,0x0651,0x0634
+    }, letters_dest1[]={
+        0x0020,0xFEB7,0xFE7D,0xFEE4,0xFEB2 
+    }, letters_dest2[]={
+        0xFEB7,0xFE7D,0xFEE4,0xFEB2,0x0020
+    }, letters_dest3[]={
+        0xFEB7,0xFE7D,0xFEE4,0xFEB2
+    }, letters_dest4[]={
+        0xFEB7,0xFE7D,0xFEE4,0x0640,0xFEB2
+    }, letters_dest5[]={
+        0x0020,0xFEB2,0xFEE4,0xFE7D,0xFEB7 
+    }, letters_dest6[]={
+        0xFEB2,0xFEE4,0xFE7D,0xFEB7,0x0020
+    }, letters_dest7[]={
+        0xFEB2,0xFEE4,0xFE7D,0xFEB7
+    }, letters_dest8[]={
+        0xFEB2,0x0640,0xFEE4,0xFE7D,0xFEB7
+    };
+
+    UChar dest[20];
+    UErrorCode errorCode;
+    int32_t length;
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(letters_source1, LENGTHOF(letters_source1),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_TEXT_DIRECTION_VISUAL_RTL | U_SHAPE_TASHKEEL_BEGIN | U_SHAPE_LETTERS_SHAPE,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(letters_dest1) || memcmp(dest, letters_dest1, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(letters_source1)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(letters_source2, LENGTHOF(letters_source2),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_TEXT_DIRECTION_VISUAL_RTL | U_SHAPE_TASHKEEL_END | U_SHAPE_LETTERS_SHAPE,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(letters_dest2) || memcmp(dest, letters_dest2, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(letters_source2)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(letters_source3, LENGTHOF(letters_source3),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_TEXT_DIRECTION_VISUAL_RTL | U_SHAPE_TASHKEEL_RESIZE | U_SHAPE_LETTERS_SHAPE,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(letters_dest3) || memcmp(dest, letters_dest3, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(letters_source3)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(letters_source4, LENGTHOF(letters_source4),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_TEXT_DIRECTION_VISUAL_RTL | U_SHAPE_TASHKEEL_REPLACE_BY_TATWEEL | U_SHAPE_LETTERS_SHAPE,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(letters_dest4) || memcmp(dest, letters_dest4, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(letters_source4)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(letters_source5, LENGTHOF(letters_source5),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_TASHKEEL_BEGIN | U_SHAPE_LETTERS_SHAPE,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(letters_dest5) || memcmp(dest, letters_dest5, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(letters_source5)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(letters_source6, LENGTHOF(letters_source6),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_TASHKEEL_END | U_SHAPE_LETTERS_SHAPE,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(letters_dest6) || memcmp(dest, letters_dest6, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(letters_source6)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(letters_source7, LENGTHOF(letters_source7),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_TASHKEEL_RESIZE | U_SHAPE_LETTERS_SHAPE,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(letters_dest7) || memcmp(dest, letters_dest7, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(letters_source7)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(letters_source8, LENGTHOF(letters_source8),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_TASHKEEL_REPLACE_BY_TATWEEL | U_SHAPE_LETTERS_SHAPE,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(letters_dest8) || memcmp(dest, letters_dest8, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(letters_source8)\n");
+    }
+}
+
+static void
+doArabicShapingTestForBug9024(void) {
+    static const UChar
+    letters_source1[]={  /* Arabic mathematical Symbols 0x1EE00 - 0x1EE1B */
+        0xD83B, 0xDE00, 0xD83B, 0xDE01, 0xD83B, 0xDE02, 0xD83B, 0xDE03, 0x20, 
+        0xD83B, 0xDE24, 0xD83B, 0xDE05, 0xD83B, 0xDE06, 0x20,
+        0xD83B, 0xDE07, 0xD83B, 0xDE08, 0xD83B, 0xDE09, 0x20,
+        0xD83B, 0xDE0A, 0xD83B, 0xDE0B, 0xD83B, 0xDE0C, 0xD83B, 0xDE0D, 0x20,
+        0xD83B, 0xDE0E, 0xD83B, 0xDE0F, 0xD83B, 0xDE10, 0xD83B, 0xDE11, 0x20,
+        0xD83B, 0xDE12, 0xD83B, 0xDE13, 0xD83B, 0xDE14, 0xD83B, 0xDE15, 0x20,
+        0xD83B, 0xDE16, 0xD83B, 0xDE17, 0xD83B, 0xDE18, 0x20,
+        0xD83B, 0xDE19, 0xD83B, 0xDE1A, 0xD83B, 0xDE1B
+    }, letters_source2[]={/* Arabic mathematical Symbols - Looped Symbols, 0x1EE80 - 0x1EE9B */
+        0xD83B, 0xDE80, 0xD83B, 0xDE81, 0xD83B, 0xDE82, 0xD83B, 0xDE83, 0x20, 
+        0xD83B, 0xDE84, 0xD83B, 0xDE85, 0xD83B, 0xDE86, 0x20,
+        0xD83B, 0xDE87, 0xD83B, 0xDE88, 0xD83B, 0xDE89, 0x20,
+        0xD83B, 0xDE8B, 0xD83B, 0xDE8C, 0xD83B, 0xDE8D, 0x20,
+        0xD83B, 0xDE8E, 0xD83B, 0xDE8F, 0xD83B, 0xDE90, 0xD83B, 0xDE91, 0x20,
+        0xD83B, 0xDE92, 0xD83B, 0xDE93, 0xD83B, 0xDE94, 0xD83B, 0xDE95, 0x20,
+        0xD83B, 0xDE96, 0xD83B, 0xDE97, 0xD83B, 0xDE98, 0x20,
+        0xD83B, 0xDE99, 0xD83B, 0xDE9A, 0xD83B, 0xDE9B
+    }, letters_source3[]={/* Arabic mathematical Symbols - Double-struck Symbols, 0x1EEA1 - 0x1EEBB */
+        0xD83B, 0xDEA1, 0xD83B, 0xDEA2, 0xD83B, 0xDEA3, 0x20, 
+        0xD83B, 0xDEA5, 0xD83B, 0xDEA6, 0x20,
+        0xD83B, 0xDEA7, 0xD83B, 0xDEA8, 0xD83B, 0xDEA9, 0x20,
+        0xD83B, 0xDEAB, 0xD83B, 0xDEAC, 0xD83B, 0xDEAD, 0x20,
+        0xD83B, 0xDEAE, 0xD83B, 0xDEAF, 0xD83B, 0xDEB0, 0xD83B, 0xDEB1, 0x20,
+        0xD83B, 0xDEB2, 0xD83B, 0xDEB3, 0xD83B, 0xDEB4, 0xD83B, 0xDEB5, 0x20,
+        0xD83B, 0xDEB6, 0xD83B, 0xDEB7, 0xD83B, 0xDEB8, 0x20,
+        0xD83B, 0xDEB9, 0xD83B, 0xDEBA, 0xD83B, 0xDEBB
+    }, letters_source4[]={/* Arabic mathematical Symbols - Initial Symbols, 0x1EE21 - 0x1EE3B */
+        0xD83B, 0xDE21, 0xD83B, 0xDE22, 0x20, 
+        0xD83B, 0xDE27, 0xD83B, 0xDE29, 0x20,
+        0xD83B, 0xDE2A, 0xD83B, 0xDE2B, 0xD83B, 0xDE2C, 0xD83B, 0xDE2D, 0x20,
+        0xD83B, 0xDE2E, 0xD83B, 0xDE2F, 0xD83B, 0xDE30, 0xD83B, 0xDE31, 0x20,
+        0xD83B, 0xDE32, 0xD83B, 0xDE34, 0xD83B, 0xDE35, 0x20,
+        0xD83B, 0xDE36, 0xD83B, 0xDE37, 0x20,
+        0xD83B, 0xDE39, 0xD83B, 0xDE3B
+    }, letters_source5[]={/* Arabic mathematical Symbols - Tailed Symbols */
+        0xD83B, 0xDE42, 0xD83B, 0xDE47, 0xD83B, 0xDE49, 0xD83B, 0xDE4B, 0x20, 
+        0xD83B, 0xDE4D, 0xD83B, 0xDE4E, 0xD83B, 0xDE4F, 0x20,
+        0xD83B, 0xDE51, 0xD83B, 0xDE52, 0xD83B, 0xDE54, 0xD83B, 0xDE57, 0x20,
+        0xD83B, 0xDE59, 0xD83B, 0xDE5B, 0xD83B, 0xDE5D, 0xD83B, 0xDE5F
+    }, letters_source6[]={/* Arabic mathematical Symbols - Stretched Symbols with 06 range */
+        0xD83B, 0xDE21, 0x0633, 0xD83B, 0xDE62, 0x0647
+    }, letters_dest1[]={
+        0xD83B, 0xDE00, 0xD83B, 0xDE01, 0xD83B, 0xDE02, 0xD83B, 0xDE03, 0x20, 
+        0xD83B, 0xDE24, 0xD83B, 0xDE05, 0xD83B, 0xDE06, 0x20,
+        0xD83B, 0xDE07, 0xD83B, 0xDE08, 0xD83B, 0xDE09, 0x20,
+        0xD83B, 0xDE0A, 0xD83B, 0xDE0B, 0xD83B, 0xDE0C, 0xD83B, 0xDE0D, 0x20,
+        0xD83B, 0xDE0E, 0xD83B, 0xDE0F, 0xD83B, 0xDE10, 0xD83B, 0xDE11, 0x20,
+        0xD83B, 0xDE12, 0xD83B, 0xDE13, 0xD83B, 0xDE14, 0xD83B, 0xDE15, 0x20,
+        0xD83B, 0xDE16, 0xD83B, 0xDE17, 0xD83B, 0xDE18, 0x20,
+        0xD83B, 0xDE19, 0xD83B, 0xDE1A, 0xD83B, 0xDE1B
+    }, letters_dest2[]={
+        0xD83B, 0xDE80, 0xD83B, 0xDE81, 0xD83B, 0xDE82, 0xD83B, 0xDE83, 0x20, 
+        0xD83B, 0xDE84, 0xD83B, 0xDE85, 0xD83B, 0xDE86, 0x20,
+        0xD83B, 0xDE87, 0xD83B, 0xDE88, 0xD83B, 0xDE89, 0x20,
+        0xD83B, 0xDE8B, 0xD83B, 0xDE8C, 0xD83B, 0xDE8D, 0x20,
+        0xD83B, 0xDE8E, 0xD83B, 0xDE8F, 0xD83B, 0xDE90, 0xD83B, 0xDE91, 0x20,
+        0xD83B, 0xDE92, 0xD83B, 0xDE93, 0xD83B, 0xDE94, 0xD83B, 0xDE95, 0x20,
+        0xD83B, 0xDE96, 0xD83B, 0xDE97, 0xD83B, 0xDE98, 0x20,
+        0xD83B, 0xDE99, 0xD83B, 0xDE9A, 0xD83B, 0xDE9B
+    }, letters_dest3[]={
+        0xD83B, 0xDEA1, 0xD83B, 0xDEA2, 0xD83B, 0xDEA3, 0x20, 
+        0xD83B, 0xDEA5, 0xD83B, 0xDEA6, 0x20,
+        0xD83B, 0xDEA7, 0xD83B, 0xDEA8, 0xD83B, 0xDEA9, 0x20,
+        0xD83B, 0xDEAB, 0xD83B, 0xDEAC, 0xD83B, 0xDEAD, 0x20,
+        0xD83B, 0xDEAE, 0xD83B, 0xDEAF, 0xD83B, 0xDEB0, 0xD83B, 0xDEB1, 0x20,
+        0xD83B, 0xDEB2, 0xD83B, 0xDEB3, 0xD83B, 0xDEB4, 0xD83B, 0xDEB5, 0x20,
+        0xD83B, 0xDEB6, 0xD83B, 0xDEB7, 0xD83B, 0xDEB8, 0x20,
+        0xD83B, 0xDEB9, 0xD83B, 0xDEBA, 0xD83B, 0xDEBB
+    }, letters_dest4[]={
+        0xD83B, 0xDE21, 0xD83B, 0xDE22, 0x20, 
+        0xD83B, 0xDE27, 0xD83B, 0xDE29, 0x20,
+        0xD83B, 0xDE2A, 0xD83B, 0xDE2B, 0xD83B, 0xDE2C, 0xD83B, 0xDE2D, 0x20,
+        0xD83B, 0xDE2E, 0xD83B, 0xDE2F, 0xD83B, 0xDE30, 0xD83B, 0xDE31, 0x20,
+        0xD83B, 0xDE32, 0xD83B, 0xDE34, 0xD83B, 0xDE35, 0x20,
+        0xD83B, 0xDE36, 0xD83B, 0xDE37, 0x20,
+        0xD83B, 0xDE39, 0xD83B, 0xDE3B
+    }, letters_dest5[]={
+        0xD83B, 0xDE42, 0xD83B, 0xDE47, 0xD83B, 0xDE49, 0xD83B, 0xDE4B, 0x20, 
+        0xD83B, 0xDE4D, 0xD83B, 0xDE4E, 0xD83B, 0xDE4F, 0x20,
+        0xD83B, 0xDE51, 0xD83B, 0xDE52, 0xD83B, 0xDE54, 0xD83B, 0xDE57, 0x20,
+        0xD83B, 0xDE59, 0xD83B, 0xDE5B, 0xD83B, 0xDE5D, 0xD83B, 0xDE5F
+    }, letters_dest6[]={
+        0xD83B, 0xDE21, 0xFEB1, 0xD83B, 0xDE62, 0xFEE9
+    };
+
+    UChar dest[MAXLEN];
+    UErrorCode errorCode;
+    int32_t length;
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(letters_source1, LENGTHOF(letters_source1),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_TEXT_DIRECTION_VISUAL_RTL | U_SHAPE_TASHKEEL_BEGIN | U_SHAPE_LETTERS_SHAPE,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(letters_dest1) || memcmp(dest, letters_dest1, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(letters_source1)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(letters_source2, LENGTHOF(letters_source2),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_TEXT_DIRECTION_VISUAL_RTL | U_SHAPE_TASHKEEL_END | U_SHAPE_LETTERS_SHAPE,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(letters_dest2) || memcmp(dest, letters_dest2, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(letters_source2)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(letters_source3, LENGTHOF(letters_source3),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_TEXT_DIRECTION_VISUAL_RTL | U_SHAPE_TASHKEEL_RESIZE | U_SHAPE_LETTERS_SHAPE,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(letters_dest3) || memcmp(dest, letters_dest3, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(letters_source3)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(letters_source4, LENGTHOF(letters_source4),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_TEXT_DIRECTION_VISUAL_RTL | U_SHAPE_TASHKEEL_REPLACE_BY_TATWEEL | U_SHAPE_LETTERS_SHAPE,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(letters_dest4) || memcmp(dest, letters_dest4, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(letters_source4)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(letters_source5, LENGTHOF(letters_source5),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_TASHKEEL_BEGIN | U_SHAPE_LETTERS_SHAPE,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(letters_dest5) || memcmp(dest, letters_dest5, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(letters_source5)\n");
+    }
+
+    errorCode=U_ZERO_ERROR;
+
+    length=u_shapeArabic(letters_source6, LENGTHOF(letters_source6),
+                         dest, LENGTHOF(dest),
+                         U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_TASHKEEL_END | U_SHAPE_LETTERS_SHAPE,
+                         &errorCode);
+
+    if(U_FAILURE(errorCode) || length!=LENGTHOF(letters_dest6) || memcmp(dest, letters_dest6, length*U_SIZEOF_UCHAR)!=0) {
+        log_err("failure in u_shapeArabic(letters_source6)\n");
+    }
+
+}
+
+/* helpers ------------------------------------------------------------------ */
+
+static void initCharFromDirProps(void) {
     static const UVersionInfo ucd401={ 4, 0, 1, 0 };
     static UVersionInfo ucdVersion={ 0, 0, 0, 0 };
 
@@ -1875,8 +3346,7 @@ getStringFromDirProps(const uint8_t *dirProps, int32_t length, UChar *buffer) {
     return buffer;
 }
 
-static void
-printUnicode(const UChar *s, int32_t length, const UBiDiLevel *levels) {
+static void printUnicode(const UChar *s, int32_t length, const UBiDiLevel *levels) {
     int32_t i;
 
     log_verbose("{ ");
@@ -1936,7 +3406,7 @@ getBiDiObject(void) {
 #define MAKE_ITEMS(val) val, #val
 
 static const struct {
-    uint32_t value;
+    UBiDiReorderingMode value;
     const char* description;
 }
 modes[] = {
@@ -1945,7 +3415,11 @@ modes[] = {
     { MAKE_ITEMS(UBIDI_REORDER_NUMBERS_SPECIAL) },
     { MAKE_ITEMS(UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL) },
     { MAKE_ITEMS(UBIDI_REORDER_INVERSE_NUMBERS_AS_L) }
-},
+};
+static const struct {
+    uint32_t value;
+    const char* description;
+}
 options[] = {
     { MAKE_ITEMS(UBIDI_OPTION_INSERT_MARKS) },
     { MAKE_ITEMS(0) }
@@ -2356,6 +3830,7 @@ assertRoundTrip(UBiDi *pBiDi, int32_t tc, int32_t outIndex, const char *srcChars
 
     u16ToPseudo(destLen, dest, destChars3);
     u16ToPseudo(destLen2, dest2, destChars2);
+    checkWhatYouCan(pBiDi, destChars3, destChars2);
     if (strcmp(srcChars, destChars2)) {
         if (roundtrip[tc][mode][option][level]) {
             log_err("\nRound trip failed for case=%d mode=%d option=%d.\n"
@@ -2381,13 +3856,13 @@ assertRoundTrip(UBiDi *pBiDi, int32_t tc, int32_t outIndex, const char *srcChars
         }
         return FALSE;
     }
-    if (!checkResultLength(pBiDi, destChars, destChars2, dest2, destLen2,
+    if (!checkResultLength(pBiDi, destChars, destChars2, destLen2,
                            desc, "UBIDI_OPTION_REMOVE_CONTROLS", level)) {
         return FALSE;
     }
-    if (outIndex > -1 && !testMaps(pBiDi, outIndex, srcChars, destChars,
-                                      desc, "UBIDI_OPTION_REMOVE_CONTROLS",
-                                      level, FALSE)) {
+    if (outIndex > -1 && !checkMaps(pBiDi, outIndex, srcChars, destChars,
+                                    desc, "UBIDI_OPTION_REMOVE_CONTROLS",
+                                    level, FALSE)) {
         return FALSE;
     }
     return TRUE;
@@ -2395,7 +3870,7 @@ assertRoundTrip(UBiDi *pBiDi, int32_t tc, int32_t outIndex, const char *srcChars
 
 static UBool
 checkResultLength(UBiDi *pBiDi, const char *srcChars, const char *destChars,
-                  const UChar *dest, int32_t destLen, const char* mode,
+                  int32_t destLen, const char* mode,
                   const char* option, UBiDiLevel level) {
     int32_t actualLen;
     if (strcmp(mode, "UBIDI_REORDER_INVERSE_NUMBERS_AS_L") == 0)
@@ -2415,54 +3890,59 @@ checkResultLength(UBiDi *pBiDi, const char *srcChars, const char *destChars,
 }
 
 static void
-doReorderRunsTest(void) {
+testReorderRunsOnly(void) {
     static const struct {
         const char* textIn;
         const char* textOut[2][2];
         const char noroundtrip[2];
     } testCases[] = {
-        {"ab 234 896 de", {{"de 896 ab 234", "de 896 ab 234"},
+        {"ab 234 896 de", {{"de 896 ab 234", "de 896 ab 234"},                   /*0*/
                            {"ab 234 @896@ de", "de 896 ab 234"}}, {0, 0}},
-        {"abcGHI", {{"GHIabc", "GHIabc"}, {"GHIabc", "GHIabc"}}, {0, 0}},
-        {"a.>67->", {{"<-67<.a", "<-67<.a"}, {"<-67<.a", "<-67<.a"}}, {0, 0}},
-        {"-=%$123/ *", {{"* /%$123=-", "* /%$123=-"},
+        {"abcGHI", {{"GHIabc", "GHIabc"}, {"GHIabc", "GHIabc"}}, {0, 0}},        /*1*/
+        {"a.>67->", {{"<-67<.a", "<-67<.a"}, {"<-67<.a", "<-67<.a"}}, {0, 0}},   /*2*/
+        {"-=%$123/ *", {{"* /%$123=-", "* /%$123=-"},                            /*3*/
                         {"* /%$123=-", "* /%$123=-"}}, {0, 0}},
-        {"abc->12..>JKL", {{"JKL<..12<-abc", "JKL<..abc->12"},
+        {"abc->12..>JKL", {{"JKL<..12<-abc", "JKL<..abc->12"},                   /*4*/
                            {"JKL<..12<-abc", "JKL<..abc->12"}}, {0, 0}},
-        {"JKL->12..>abc", {{"abc<..JKL->12", "abc<..12<-JKL"},
+        {"JKL->12..>abc", {{"abc<..JKL->12", "abc<..12<-JKL"},                   /*5*/
                            {"abc<..JKL->12", "abc<..12<-JKL"}}, {0, 0}},
-        {"123->abc", {{"abc<-123", "abc<-123"},
+        {"123->abc", {{"abc<-123", "abc<-123"},                                  /*6*/
                       {"abc&<-123", "abc<-123"}}, {1, 0}},
-        {"123->JKL", {{"JKL<-123", "123->JKL"},
+        {"123->JKL", {{"JKL<-123", "123->JKL"},                                  /*7*/
                       {"JKL<-123", "JKL<-@123"}}, {0, 1}},
-        {"*>12.>34->JKL", {{"JKL<-34<.12<*", "12.>34->JKL<*"},
+        {"*>12.>34->JKL", {{"JKL<-34<.12<*", "12.>34->JKL<*"},                   /*8*/
                            {"JKL<-34<.12<*", "JKL<-@34<.12<*"}}, {0, 1}},
-        {"*>67.>89->JKL", {{"67.>89->JKL<*", "67.>89->JKL<*"},
+        {"*>67.>89->JKL", {{"67.>89->JKL<*", "67.>89->JKL<*"},                   /*9*/
                            {"67.>89->JKL<*", "67.>89->JKL<*"}}, {0, 0}},
-        {"* /abc-=$%123", {{"$%123=-abc/ *", "abc-=$%123/ *"},
+        {"* /abc-=$%123", {{"$%123=-abc/ *", "abc-=$%123/ *"},                   /*10*/
                            {"$%123=-abc/ *", "abc-=$%123/ *"}}, {0, 0}},
-        {"* /$%def-=123", {{"123=-def%$/ *", "def-=123%$/ *"},
+        {"* /$%def-=123", {{"123=-def%$/ *", "def-=123%$/ *"},                   /*11*/
                            {"123=-def%$/ *", "def-=123%$/ *"}}, {0, 0}},
-        {"-=GHI* /123%$", {{"GHI* /123%$=-", "123%$/ *GHI=-"},
+        {"-=GHI* /123%$", {{"GHI* /123%$=-", "123%$/ *GHI=-"},                   /*12*/
                            {"GHI* /123%$=-", "123%$/ *GHI=-"}}, {0, 0}},
-        {"-=%$JKL* /123", {{"JKL* /%$123=-", "123/ *JKL$%=-"},
+        {"-=%$JKL* /123", {{"JKL* /%$123=-", "123/ *JKL$%=-"},                   /*13*/
                            {"JKL* /%$123=-", "123/ *JKL$%=-"}}, {0, 0}},
-        {"ab =#CD *?450", {{"CD *?450#= ab", "450?* CD#= ab"},
+        {"ab =#CD *?450", {{"CD *?450#= ab", "450?* CD#= ab"},                   /*14*/
                            {"CD *?450#= ab", "450?* CD#= ab"}}, {0, 0}},
-        {"ab 234 896 de", {{"de 896 ab 234", "de 896 ab 234"},
+        {"ab 234 896 de", {{"de 896 ab 234", "de 896 ab 234"},                   /*15*/
                            {"ab 234 @896@ de", "de 896 ab 234"}}, {0, 0}},
-        {"abc-=%$LMN* /123", {{"LMN* /%$123=-abc", "123/ *LMN$%=-abc"},
+        {"abc-=%$LMN* /123", {{"LMN* /%$123=-abc", "123/ *LMN$%=-abc"},          /*16*/
                               {"LMN* /%$123=-abc", "123/ *LMN$%=-abc"}}, {0, 0}},
-        {"123->JKL&MN&P", {{"JKLMNP<-123", "123->JKLMNP"},
-                           {"JKLMNP<-123", "JKLMNP<-@123"}}, {0, 1}}
+        {"123->JKL&MN&P", {{"JKLMNP<-123", "123->JKLMNP"},                       /*17*/
+                           {"JKLMNP<-123", "JKLMNP<-@123"}}, {0, 1}},
+        {"123", {{"123", "123"},                /* just one run */               /*18*/
+                 {"123", "123"}}, {0, 0}}
     };
     UBiDi *pBiDi = getBiDiObject();
     UBiDi *pL2VBiDi = getBiDiObject();
     UChar src[MAXLEN], dest[MAXLEN], visual1[MAXLEN], visual2[MAXLEN];
     char destChars[MAXLEN], vis1Chars[MAXLEN], vis2Chars[MAXLEN];
-    int32_t srcLen, destLen, vis1Len, vis2Len, option, i, j, nCases;
+    int32_t srcLen, destLen, vis1Len, vis2Len, option, i, j, nCases, paras;
     UErrorCode rc = U_ZERO_ERROR;
     UBiDiLevel level;
+
+    log_verbose("\nEntering TestReorderRunsOnly\n\n");
+
     if(!pL2VBiDi) {
         ubidi_close(pBiDi);             /* in case this one was allocated */
         return;
@@ -2477,6 +3957,8 @@ doReorderRunsTest(void) {
             srcLen = strlen(testCases[i].textIn);
             pseudoToU16(srcLen, testCases[i].textIn, src);
             for(j = 0; j < 2; j++) {
+                log_verbose("Now doing test for option %d, case %d, level %d\n",
+                            i, option, j);
                 level = paraLevels[j];
                 ubidi_setPara(pBiDi, src, srcLen, level, NULL, &rc);
                 assertSuccessful("ubidi_setPara", &rc);
@@ -2484,6 +3966,7 @@ doReorderRunsTest(void) {
                 destLen = ubidi_writeReordered(pBiDi, dest, MAXLEN, UBIDI_DO_MIRRORING, &rc);
                 assertSuccessful("ubidi_writeReordered", &rc);
                 u16ToPseudo(destLen, dest, destChars);
+                checkWhatYouCan(pBiDi, testCases[i].textIn, destChars);
                 assertStringsEqual(testCases[i].textOut[option][level], destChars,
                         testCases[i].textIn, "UBIDI_REORDER_RUNS_ONLY",
                         option==0 ? "0" : "UBIDI_OPTION_INSERT_MARKS",
@@ -2498,12 +3981,14 @@ doReorderRunsTest(void) {
                 vis1Len = ubidi_writeReordered(pL2VBiDi, visual1, MAXLEN, UBIDI_DO_MIRRORING, &rc);
                 assertSuccessful("ubidi_writeReordered1", &rc);
                 u16ToPseudo(vis1Len, visual1, vis1Chars);
+                checkWhatYouCan(pL2VBiDi, testCases[i].textIn, vis1Chars);
                 ubidi_setPara(pL2VBiDi, dest, destLen, level^1, NULL, &rc);
                 assertSuccessful("ubidi_setPara2", &rc);
                 *visual2 = 0;
                 vis2Len = ubidi_writeReordered(pL2VBiDi, visual2, MAXLEN, UBIDI_DO_MIRRORING, &rc);
                 assertSuccessful("ubidi_writeReordered2", &rc);
                 u16ToPseudo(vis2Len, visual2, vis2Chars);
+                checkWhatYouCan(pL2VBiDi, destChars, vis2Chars);
                 assertStringsEqual(vis1Chars, vis2Chars,
                         testCases[i].textIn, "UBIDI_REORDER_RUNS_ONLY (2)",
                         option==0 ? "0" : "UBIDI_OPTION_INSERT_MARKS",
@@ -2511,24 +3996,36 @@ doReorderRunsTest(void) {
             }
         }
     }
+
+    /* test with null or empty text */
+    ubidi_setPara(pBiDi, src, 0, UBIDI_LTR, NULL, &rc);
+    assertSuccessful("ubidi_setPara3", &rc);
+    paras = ubidi_countParagraphs(pBiDi);
+    if (paras != 0) {
+        log_err("\nInvalid number of paras (should be 0): %d\n", paras);
+    }
+
     ubidi_close(pBiDi);
     ubidi_close(pL2VBiDi);
+
+    log_verbose("\nExiting TestReorderRunsOnly\n\n");
 }
 
 static void
-doReorderingModeBidiTest() {
+testReorderingMode(void) {
 
     UChar src[MAXLEN], dest[MAXLEN];
     char destChars[MAXLEN];
     UBiDi *pBiDi = NULL, *pBiDi2 = NULL, *pBiDi3 = NULL;
     UErrorCode rc;
     int tc, mode, option, level;
-    uint32_t modeValue, modeBack, optionValue, optionBack;
-    int32_t srcLen, destLen, index;
+    uint32_t optionValue, optionBack;
+    UBiDiReorderingMode modeValue, modeBack;
+    int32_t srcLen, destLen, idx;
     const char *expectedChars;
     UBool testOK = TRUE;
 
-    log_verbose("\n*** Bidi reordering mode test ***\n");
+    log_verbose("\nEntering TestReorderingMode\n\n");
 
     pBiDi = getBiDiObject();
     pBiDi2 = getBiDiObject();
@@ -2542,7 +4039,7 @@ doReorderingModeBidiTest() {
     ubidi_setInverse(pBiDi2, TRUE);
 
     for (tc = 0; tc < TC_COUNT; tc++) {
-        const charsrcChars = textIn[tc];
+        const char *srcChars = textIn[tc];
         srcLen = strlen(srcChars);
         pseudoToU16(srcLen, srcChars, src);
 
@@ -2576,15 +4073,19 @@ doReorderingModeBidiTest() {
                                                    UBIDI_DO_MIRRORING, &rc);
                     assertSuccessful("ubidi_writeReordered", &rc);
                     u16ToPseudo(destLen, dest, destChars);
+                    if (!((modes[mode].value == UBIDI_REORDER_INVERSE_NUMBERS_AS_L) &&
+                          (options[option].value == UBIDI_OPTION_INSERT_MARKS))) {
+                        checkWhatYouCan(pBiDi, srcChars, destChars);
+                    }
 
                     if (modes[mode].value == UBIDI_REORDER_INVERSE_NUMBERS_AS_L) {
-                        index = -1;
-                        expectedChars = inverseBasic(pBiDi2, src, srcLen,
+                        idx = -1;
+                        expectedChars = inverseBasic(pBiDi2, srcChars, srcLen,
                                 options[option].value, paraLevels[level], destChars);
                     }
                     else {
-                        index = outIndices[tc][mode][option][level];
-                        expectedChars = textOut[index];
+                        idx = outIndices[tc][mode][option][level];
+                        expectedChars = textOut[idx];
                     }
                     if (!assertStringsEqual(expectedChars, destChars, srcChars,
                                 modes[mode].description,
@@ -2592,19 +4093,19 @@ doReorderingModeBidiTest() {
                                 pBiDi)) {
                         testOK = FALSE;
                     }
-                    else if (options[option].value == UBIDI_OPTION_INSERT_MARKS &&
-                             !assertRoundTrip(pBiDi3, tc, index, srcChars,
+                    if (options[option].value == UBIDI_OPTION_INSERT_MARKS &&
+                             !assertRoundTrip(pBiDi3, tc, idx, srcChars,
                                               destChars, dest, destLen,
                                               mode, option, paraLevels[level])) {
                         testOK = FALSE;
                     }
                     else if (!checkResultLength(pBiDi, srcChars, destChars,
-                                dest, destLen, modes[mode].description,
+                                destLen, modes[mode].description,
                                 options[option].description,
                                 paraLevels[level])) {
                         testOK = FALSE;
                     }
-                    else if (index > -1 && !testMaps(pBiDi, index, srcChars,
+                    else if (idx > -1 && !checkMaps(pBiDi, idx, srcChars,
                             destChars, modes[mode].description,
                             options[option].description, paraLevels[level],
                             TRUE)) {
@@ -2620,18 +4121,21 @@ doReorderingModeBidiTest() {
     ubidi_close(pBiDi3);
     ubidi_close(pBiDi2);
     ubidi_close(pBiDi);
+
+    log_verbose("\nExiting TestReorderingMode\n\n");
 }
 
-static const char* inverseBasic(UBiDi *pBiDi, const UChar *src, int32_t srcLen,
+static const char* inverseBasic(UBiDi *pBiDi, const char *srcChars, int32_t srcLen,
                                 uint32_t option, UBiDiLevel level, char *result) {
     UErrorCode rc = U_ZERO_ERROR;
     int32_t destLen;
-    UChar dest2 [MAXLEN];
+    UChar src[MAXLEN], dest2[MAXLEN];
 
     if (pBiDi == NULL || src == NULL) {
         return NULL;
     }
     ubidi_setReorderingOptions(pBiDi, option);
+    pseudoToU16(srcLen, srcChars, src);
     ubidi_setPara(pBiDi, src, srcLen, level, NULL, &rc);
     assertSuccessful("ubidi_setPara", &rc);
 
@@ -2640,13 +4144,16 @@ static const char* inverseBasic(UBiDi *pBiDi, const UChar *src, int32_t srcLen,
                                    UBIDI_DO_MIRRORING, &rc);
     assertSuccessful("ubidi_writeReordered", &rc);
     u16ToPseudo(destLen, dest2, result);
+    if (!(option == UBIDI_OPTION_INSERT_MARKS)) {
+        checkWhatYouCan(pBiDi, srcChars, result);
+    }
     return result;
 }
 
 #define NULL_CHAR '\0'
 
 static void
-doBidiStreamingTest() {
+testStreaming(void) {
 #define MAXPORTIONS 10
 
     static const struct {
@@ -2689,7 +4196,7 @@ doBidiStreamingTest() {
     UBool mismatch, testOK = TRUE;
     char processedLenStr[MAXPORTIONS * 5];
 
-    log_verbose("\n*** Bidi streaming test ***\n");
+    log_verbose("\nEntering TestStreaming\n\n");
 
     pBiDi = getBiDiObject();
 
@@ -2702,6 +4209,7 @@ doBidiStreamingTest() {
             nPortions = testData[i].nPortions[levelIndex];
             level = paraLevels[levelIndex];
             *processedLenStr = NULL_CHAR;
+            log_verbose("Testing level %d, case %d\n", level, i);
 
             mismatch = FALSE;
 
@@ -2710,7 +4218,9 @@ doBidiStreamingTest() {
 
                 len = chunk < srcLen ? chunk : srcLen;
                 ubidi_setPara(pBiDi, pSrc, len, level, NULL, &rc);
-                assertSuccessful("ubidi_setPara", &rc);
+                if (!assertSuccessful("ubidi_setPara", &rc)) {
+                    break;
+                }
 
                 processedLen = ubidi_getProcessedLength(pBiDi);
                 if (processedLen == 0) {
@@ -2720,8 +4230,8 @@ doBidiStreamingTest() {
                 }
                 ubidi_setReorderingOptions(pBiDi, UBIDI_OPTION_STREAMING);
 
-                mismatch = j >= nPortions ||
-                           processedLen != testData[i].portionLens[levelIndex][j];
+                mismatch = (UBool)(j >= nPortions ||
+                           processedLen != testData[i].portionLens[levelIndex][j]);
 
                 sprintf(processedLenStr + j * 4, "%4d", processedLen);
                 srcLen -= processedLen, pSrc += processedLen;
@@ -2743,6 +4253,7 @@ doBidiStreamingTest() {
     if (testOK == TRUE) {
         log_verbose("\nBiDi streaming test OK\n");
     }
+    log_verbose("\nExiting TestStreaming\n\n");
 }
 
 U_CDECL_BEGIN
@@ -2772,6 +4283,8 @@ overrideBidiClass(const void *context, UChar32 c) {
           DEF,   DEF,   DEF,   LRO,     B,   RLO,    BN,   DEF  /* 78-7F */
     };
     static const int nEntries = LENGTHOF(customClasses);
+    const char *dummy = context;        /* just to avoid a compiler warning */
+    dummy++;
 
     return c >= nEntries ? U_BIDI_CLASS_DEFAULT : customClasses[c];
 }
@@ -2794,7 +4307,8 @@ static void verifyCallbackParams(UBiDiClassCallback* fn, const void* context,
     }
 }
 
-static void doBidiClassOverrideTest(void) {
+static void
+testClassOverride(void) {
     static const char* const textSrc  = "JIH.>12->a \\u05D0\\u05D1 6 ABC78";
     static const char* const textResult = "12<.HIJ->a 78CBA 6 \\u05D1\\u05D0";
 
@@ -2807,7 +4321,7 @@ static void doBidiClassOverrideTest(void) {
     int32_t srcLen, destLen, textSrcSize = (int32_t)uprv_strlen(textSrc);
     char* destChars = NULL;
 
-    log_verbose("\n*** Bidi class override test ***\n");
+    log_verbose("\nEntering TestClassOverride\n\n");
 
     pBiDi = getBiDiObject();
     if(!pBiDi) {
@@ -2853,6 +4367,7 @@ static void doBidiClassOverrideTest(void) {
         log_verbose("\nClass override test OK\n");
     }
     ubidi_close(pBiDi);
+    log_verbose("\nExiting TestClassOverride\n\n");
 }
 
 static char * formatMap(const int32_t * map, int len, char * buffer)
@@ -2863,7 +4378,7 @@ static char * formatMap(const int32_t * map, int len, char * buffer)
         k = map[i];
         if (k < 0)
             c = '-';
-        else if (k >= sizeof columns)
+        else if (k >= sizeof(columns))
             c = '+';
         else
             c = columns[k];
@@ -2874,13 +4389,13 @@ static char * formatMap(const int32_t * map, int len, char * buffer)
 }
 
 static UBool
-testMaps(UBiDi *pBiDi, int32_t stringIndex, const char *src, const char *dest,
-         const char *mode, const char* option, UBiDiLevel level, UBool forward)
+checkMaps(UBiDi *pBiDi, int32_t stringIndex, const char *src, const char *dest,
+          const char *mode, const char* option, UBiDiLevel level, UBool forward)
 {
     int32_t actualLogicalMap[MAX_MAP_LENGTH];
     int32_t actualVisualMap[MAX_MAP_LENGTH];
     int32_t getIndexMap[MAX_MAP_LENGTH];
-    int32_t i, srcLen, resLen, index;
+    int32_t i, srcLen, resLen, idx;
     const int32_t *expectedLogicalMap, *expectedVisualMap;
     UErrorCode rc = U_ZERO_ERROR;
     UBool testOK = TRUE;
@@ -2950,9 +4465,9 @@ testMaps(UBiDi *pBiDi, int32_t stringIndex, const char *src, const char *dest,
         testOK = FALSE;
     }
     for (i = 0; i < srcLen; i++) {
-        index = ubidi_getVisualIndex(pBiDi, i, &rc);
+        idx = ubidi_getVisualIndex(pBiDi, i, &rc);
         assertSuccessful("ubidi_getVisualIndex", &rc);
-        getIndexMap[i] = index;
+        getIndexMap[i] = idx;
     }
     if (memcmp(actualLogicalMap, getIndexMap, srcLen * sizeof(int32_t))) {
         char actChars[MAX_MAP_LENGTH];
@@ -2979,9 +4494,9 @@ testMaps(UBiDi *pBiDi, int32_t stringIndex, const char *src, const char *dest,
         testOK = FALSE;
     }
     for (i = 0; i < resLen; i++) {
-        index = ubidi_getLogicalIndex(pBiDi, i, &rc);
+        idx = ubidi_getLogicalIndex(pBiDi, i, &rc);
         assertSuccessful("ubidi_getLogicalIndex", &rc);
-        getIndexMap[i] = index;
+        getIndexMap[i] = idx;
     }
     if (memcmp(actualVisualMap, getIndexMap, resLen * sizeof(int32_t))) {
         char actChars[MAX_MAP_LENGTH];
@@ -3010,3 +4525,132 @@ testMaps(UBiDi *pBiDi, int32_t stringIndex, const char *src, const char *dest,
     return testOK;
 }
 
+static UBool
+assertIllegalArgument(const char* message, UErrorCode* rc) {
+    if (*rc != U_ILLEGAL_ARGUMENT_ERROR) {
+        log_err("%s() failed with error %s.\n", message, myErrorName(*rc));
+        return FALSE;
+    }
+    return TRUE;
+}
+
+typedef struct {
+    const char* prologue;
+    const char* source;
+    const char* epilogue;
+    const char* expected;
+    UBiDiLevel paraLevel;
+} contextCase;
+
+static const contextCase contextData[] = {
+    /*00*/  {"", "", "", "", UBIDI_LTR},
+    /*01*/  {"", ".-=JKL-+*", "", ".-=LKJ-+*", UBIDI_LTR},
+    /*02*/  {" ", ".-=JKL-+*", " ", ".-=LKJ-+*", UBIDI_LTR},
+    /*03*/  {"a", ".-=JKL-+*", "b", ".-=LKJ-+*", UBIDI_LTR},
+    /*04*/  {"D", ".-=JKL-+*", "", "LKJ=-.-+*", UBIDI_LTR},
+    /*05*/  {"", ".-=JKL-+*", " D", ".-=*+-LKJ", UBIDI_LTR},
+    /*06*/  {"", ".-=JKL-+*", " 2", ".-=*+-LKJ", UBIDI_LTR},
+    /*07*/  {"", ".-=JKL-+*", " 7", ".-=*+-LKJ", UBIDI_LTR},
+    /*08*/  {" G 1", ".-=JKL-+*", " H", "*+-LKJ=-.", UBIDI_LTR},
+    /*09*/  {"7", ".-=JKL-+*", " H", ".-=*+-LKJ", UBIDI_LTR},
+    /*10*/  {"", ".-=abc-+*", "", "*+-abc=-.", UBIDI_RTL},
+    /*11*/  {" ", ".-=abc-+*", " ", "*+-abc=-.", UBIDI_RTL},
+    /*12*/  {"D", ".-=abc-+*", "G", "*+-abc=-.", UBIDI_RTL},
+    /*13*/  {"x", ".-=abc-+*", "", "*+-.-=abc", UBIDI_RTL},
+    /*14*/  {"", ".-=abc-+*", " y", "abc-+*=-.", UBIDI_RTL},
+    /*15*/  {"", ".-=abc-+*", " 2", "abc-+*=-.", UBIDI_RTL},
+    /*16*/  {" x 1", ".-=abc-+*", " 2", ".-=abc-+*", UBIDI_RTL},
+    /*17*/  {" x 7", ".-=abc-+*", " 8", "*+-.-=abc", UBIDI_RTL},
+    /*18*/  {"x|", ".-=abc-+*", " 8", "*+-abc=-.", UBIDI_RTL},
+    /*19*/  {"G|y", ".-=abc-+*", " 8", "*+-.-=abc", UBIDI_RTL},
+    /*20*/  {"", ".-=", "", ".-=", UBIDI_DEFAULT_LTR},
+    /*21*/  {"D", ".-=", "", "=-.", UBIDI_DEFAULT_LTR},
+    /*22*/  {"G", ".-=", "", "=-.", UBIDI_DEFAULT_LTR},
+    /*23*/  {"xG", ".-=", "", ".-=", UBIDI_DEFAULT_LTR},
+    /*24*/  {"x|G", ".-=", "", "=-.", UBIDI_DEFAULT_LTR},
+    /*25*/  {"x|G", ".-=|-+*", "", "=-.|-+*", UBIDI_DEFAULT_LTR},
+};
+#define CONTEXT_COUNT       LENGTHOF(contextData)
+
+static void
+testContext(void) {
+
+    UChar prologue[MAXLEN], epilogue[MAXLEN], src[MAXLEN], dest[MAXLEN];
+    char destChars[MAXLEN];
+    UBiDi *pBiDi = NULL;
+    UErrorCode rc;
+    int32_t proLength, epiLength, srcLen, destLen, tc;
+    contextCase cc;
+    UBool testOK = TRUE;
+
+    log_verbose("\nEntering TestContext \n\n");
+
+    /* test null BiDi object */
+    rc = U_ZERO_ERROR;
+    ubidi_setContext(pBiDi, NULL, 0, NULL, 0, &rc);
+    testOK &= assertIllegalArgument("Error when BiDi object is null", &rc);
+
+    pBiDi = getBiDiObject();
+    ubidi_orderParagraphsLTR(pBiDi, TRUE);
+
+    /* test proLength < -1 */
+    rc = U_ZERO_ERROR;
+    ubidi_setContext(pBiDi, NULL, -2, NULL, 0, &rc);
+    testOK &= assertIllegalArgument("Error when proLength < -1", &rc);
+    /* test epiLength < -1 */
+    rc = U_ZERO_ERROR;
+    ubidi_setContext(pBiDi, NULL, 0, NULL, -2, &rc);
+    testOK &= assertIllegalArgument("Error when epiLength < -1", &rc);
+    /* test prologue == NULL */
+    rc = U_ZERO_ERROR;
+    ubidi_setContext(pBiDi, NULL, 3, NULL, 0, &rc);
+    testOK &= assertIllegalArgument("Prologue is NULL", &rc);
+    /* test epilogue == NULL */
+    rc = U_ZERO_ERROR;
+    ubidi_setContext(pBiDi, NULL, 0, NULL, 4, &rc);
+    testOK &= assertIllegalArgument("Epilogue is NULL", &rc);
+
+    for (tc = 0; tc < CONTEXT_COUNT; tc++) {
+        cc = contextData[tc];
+        proLength = strlen(cc.prologue);
+        pseudoToU16(proLength, cc.prologue, prologue);
+        epiLength = strlen(cc.epilogue);
+        pseudoToU16(epiLength, cc.epilogue, epilogue);
+        /* in the call below, prologue and epilogue are swapped to show
+           that the next call will override this call */
+        rc = U_ZERO_ERROR;
+        ubidi_setContext(pBiDi, epilogue, epiLength, prologue, proLength, &rc);
+        testOK &= assertSuccessful("swapped ubidi_setContext", &rc);
+        ubidi_setContext(pBiDi, prologue, -1, epilogue, -1, &rc);
+        testOK &= assertSuccessful("regular ubidi_setContext", &rc);
+        srcLen = strlen(cc.source);
+        pseudoToU16(srcLen, cc.source, src);
+        ubidi_setPara(pBiDi, src, srcLen, cc.paraLevel, NULL, &rc);
+        testOK &= assertSuccessful("ubidi_setPara", &rc);
+        destLen = ubidi_writeReordered(pBiDi, dest, MAXLEN, UBIDI_DO_MIRRORING, &rc);
+        assertSuccessful("ubidi_writeReordered", &rc);
+        u16ToPseudo(destLen, dest, destChars);
+        if (uprv_strcmp(cc.expected, destChars)) {
+            char formatChars[MAXLEN];
+            log_err("\nActual and expected output mismatch on case %d.\n"
+                "%20s %s\n%20s %s\n%20s %s\n%20s %s\n%20s %s\n%20s %s\n%20s %d\n%20s %u\n%20s %d\n",
+                tc,
+                "Prologue:", cc.prologue,
+                "Input:", cc.source,
+                "Epilogue:", cc.epilogue,
+                "Expected output:", cc.expected,
+                "Actual output:", destChars,
+                "Levels:", formatLevels(pBiDi, formatChars),
+                "Reordering mode:", ubidi_getReorderingMode(pBiDi),
+                "Paragraph level:", ubidi_getParaLevel(pBiDi),
+                "Reordering option:", ubidi_getReorderingOptions(pBiDi));
+            testOK = FALSE;
+        }
+    }
+    if (testOK == TRUE) {
+        log_verbose("\nContext test OK\n");
+    }
+    ubidi_close(pBiDi);
+
+    log_verbose("\nExiting TestContext \n\n");
+}