cbiditransformtst.c   [plain text]


/********************************************************************
 * © 2016 and later: Unicode, Inc. and others.
 * License & terms of use: http://www.unicode.org/copyright.html
 ********************************************************************/
/*   file name:  cbiditransformtst.c
 *   encoding:   UTF-8
 *   tab size:   8 (not used)
 *   indentation:4
 *
 *   created on: 2016aug21
 *   created by: Lina Kemmel
*/

#include "cintltst.h"
#include "unicode/ubidi.h"
#include "unicode/ubiditransform.h"
#include "unicode/ushape.h"
#include "unicode/ustring.h"
#include "unicode/utf16.h"

#ifdef __cplusplus
extern "C" {
#endif

#define     LATN_ZERO           0x0030
#define     ARAB_ZERO           0x0660
#define     MIN_HEB_LETTER      0x05D0
#define     MIN_ARAB_LETTER     0x0630 /* relevant to this test only */
#define     MIN_SHAPED_LETTER   0xFEAB /* relevant to this test only */

#define     STR_CAPACITY        100

#define     NUM_LETTERS         5       /* Used for arrays hereafter */
static const UChar unshapedLetters[NUM_LETTERS + 1] = {0x0630, 0, 0x0631, 0, 0x0632, 2};
static const UChar shapedLetters  [NUM_LETTERS + 1] = {0xfeab, 0, 0xfead, 0, 0xfeaf, 1};

typedef struct {
    UBiDiLevel  inLevel;
    UBiDiOrder  inOr;
    UBiDiLevel  outLevel;
    UBiDiOrder  outOr;
    const char  *pReorderNoMirror;
    const char  *pReorderAndMirror;
    const char  *pContextShapes;
    const char  *pMessage;
} UBidiTestCases;

UChar src[STR_CAPACITY] = { 0 };
UChar dest[STR_CAPACITY] = { 0 };
UChar expected[STR_CAPACITY] = { 0 };
UChar temp[STR_CAPACITY * 2] = { 0 };
char pseudo[STR_CAPACITY] = { 0 };

void addBidiTransformTest(TestNode** root);

static void testAutoDirection(void);

static void testAllTransformOptions(void);

static char* pseudoScript(const UChar *str);

static void shapeDigits(UChar *str, UChar srcZero, UChar destZero);

static void shapeLetters(UChar *str, const UChar *from, const UChar *to);

static void logResultsForDir(const UChar *srcText, const UChar *destTxt,
            const UChar *expectedTxt, UBiDiLevel inLevel, UBiDiLevel outLevel);

static void verifyResultsForAllOpt(const UBidiTestCases *pTest, const UChar *srcTxt,
            const UChar *destTxt, const char *expectedChars, uint32_t digits,
            uint32_t letters);

#if 0
static void substituteByPseudoChar(const UChar *src, char *dest,
            const UChar baseReal, const char basePseudo, const char max);


/* TODO: This code assumes the codepage is ASCII based. */

/*
 * Using the following conventions:
 * AL unshaped: A-E
 * AL shaped: F-J
 * R:  K-Z
 * EN: 0-4
 * AN: 5-9
*/
static void
substituteByPseudoChar(const UChar *src, char *dest, const UChar baseReal,
           const char basePseudo, const char max) {
    *dest = basePseudo + (*src - baseReal); /* (range math won't work on EBCDIC) */
    if (*dest > max) {
        *dest = max;
    }
}

static char*
pseudoScript(const UChar *str) {
    char *p = pseudo;
    if (str) {
        for (; *str; str++, p++) {
            switch (u_charDirection(*str)) {
                case U_RIGHT_TO_LEFT:
                    substituteByPseudoChar(str, p, MIN_HEB_LETTER, 'K', 'Z');
                    break;
                case U_RIGHT_TO_LEFT_ARABIC:
                    if (*str > 0xFE00) {
                        substituteByPseudoChar(str, p, MIN_SHAPED_LETTER, 'F', 'J');
                    } else {
                        substituteByPseudoChar(str, p, MIN_ARAB_LETTER, 'A', 'E');
                    }
                    break;
                case U_ARABIC_NUMBER:
                    substituteByPseudoChar(str, p, ARAB_ZERO, '5', '9');
                    break;
                default:
                    *p = (char)*str;
                    break;
            }
        }
    }
    *p = '\0';
    return pseudo;
}
#else
static char*
pseudoScript(const UChar *str) {
    return aescstrdup(str, -1);
}
#endif

static void
logResultsForDir(const UChar *srcTxt, const UChar *destTxt, const UChar *expectedTxt,
            UBiDiLevel inLevel, UBiDiLevel outLevel)
{
    if (u_strcmp(expectedTxt, destTxt)) {
        log_err("Unexpected transform Dest: inLevel: 0x%02x; outLevel: 0x%02x;\ninText: %s; outText: %s; expected: %s\n",
                inLevel, outLevel, pseudoScript(srcTxt), pseudoScript(destTxt), pseudoScript(expectedTxt));
    }
}

/**
 * Tests various combinations of base directions, with the input either
 * <code>UBIDI_DEFAULT_LTR</code> or <code>UBIDI_DEFAULT_RTL</code>, and the
 * output either <code>UBIDI_LTR</code> or <code>UBIDI_RTL</code>. Order is
 * always <code>UBIDI_LOGICAL</code> for the input and <code>UBIDI_VISUAL</code>
 * for the output.
 */
static void
testAutoDirection(void)
{
    static const UBiDiLevel inLevels[] = {
        UBIDI_DEFAULT_LTR, UBIDI_DEFAULT_RTL
    };
    static const UBiDiLevel outLevels[] = {
        UBIDI_LTR, UBIDI_RTL
    };
    static const char *srcTexts[] = {
        "abc \\u05d0\\u05d1",
        "... abc \\u05d0\\u05d1",
        "\\u05d0\\u05d1 abc",
        "... \\u05d0\\u05d1 abc",
        ".*:"
    };
    uint32_t nTexts = sizeof(srcTexts) / sizeof(srcTexts[0]);
    uint32_t i, nInLevels = sizeof(inLevels) / sizeof(inLevels[0]);
    uint32_t j, nOutLevels = sizeof(outLevels) / sizeof(outLevels[0]);

    UBiDi *pBidi = ubidi_open();

    UErrorCode errorCode = U_ZERO_ERROR;
    UBiDiTransform *pTransform = ubiditransform_open(&errorCode);

    while (nTexts-- > 0) {
        uint32_t srcLen;
        u_unescape(srcTexts[nTexts], src, STR_CAPACITY);
        srcLen = u_strlen(src);
        for (i = 0; i < nInLevels; i++) {
            for (j = 0; j < nOutLevels; j++) {
                ubiditransform_transform(pTransform, src, -1, dest, STR_CAPACITY - 1,
                        inLevels[i], UBIDI_LOGICAL, outLevels[j], UBIDI_VISUAL,
                        UBIDI_MIRRORING_OFF, 0, &errorCode);
                /* Use UBiDi as a model we compare to */
                ubidi_setPara(pBidi, src, srcLen, inLevels[i], NULL, &errorCode);
                ubidi_writeReordered(pBidi, expected, STR_CAPACITY, UBIDI_REORDER_DEFAULT, &errorCode);
                if (outLevels[j] == UBIDI_RTL) {
                    ubidi_writeReverse(expected, u_strlen(expected), temp, STR_CAPACITY,
                            UBIDI_OUTPUT_REVERSE, &errorCode);
                    logResultsForDir(src, dest, temp, inLevels[i], outLevels[j]);
                } else {
                    logResultsForDir(src, dest, expected, inLevels[i], outLevels[j]);
                }
            }
        }
    }
    ubidi_close(pBidi);
    ubiditransform_close(pTransform);
}

static void
shapeDigits(UChar *str, UChar srcZero, UChar destZero)
{
    UChar32 c = 0;
    uint32_t i = 0, j, length = u_strlen(str);
    while (i < length) {
        j = i;
        U16_NEXT(str, i, length, c); 
        if (c >= srcZero && c <= srcZero + 9) {
            /* length of c here is always a single UChar16 */
            str[j] = c + (destZero - srcZero);
        }
    }
}

static void
shapeLetters(UChar *str, const UChar *from, const UChar *to)
{
    uint32_t i = 0, j, length = u_strlen(expected), index;
    UChar32 c = 0;
    while (i < length) {
        j = i;
        U16_NEXT(str, i, length, c); 
        index = c - from[0];
        if (index < NUM_LETTERS && from[index * from[NUM_LETTERS]] != 0) {
            /* The length of old and new values is always a single UChar16,
               so can just assign a new value to str[j] */
            str[j] = to[index * from[NUM_LETTERS]];
        }
    }
}

static void
verifyResultsForAllOpt(const UBidiTestCases *pTest, const UChar *srcTxt,
        const UChar *destTxt, const char *expectedChars, uint32_t digits, uint32_t letters)
{
    u_unescape(expectedChars, expected, STR_CAPACITY);

    switch (digits) {
        case U_SHAPE_DIGITS_EN2AN:
            shapeDigits(expected, LATN_ZERO, ARAB_ZERO);
            break;
        case U_SHAPE_DIGITS_AN2EN:
            shapeDigits(expected, ARAB_ZERO, LATN_ZERO);
            break;
        default:
            break;
    }
    switch (letters) {
        case U_SHAPE_LETTERS_SHAPE:
            shapeLetters(expected, unshapedLetters, shapedLetters);
            break;
        case U_SHAPE_LETTERS_UNSHAPE:
            shapeLetters(expected, shapedLetters, unshapedLetters);
            break;
    }
    if (u_strcmp(expected, dest)) {
        log_err("Unexpected transform Dest: Test: %s; Digits: 0x%08x; Letters: 0x%08x\ninText: %s; outText: %s; expected: %s\n",
                pTest->pMessage, digits, letters, pseudoScript(srcTxt), pseudoScript(destTxt), pseudoScript(expected));
    }
}

/**
 * This function covers:
 * <ul>
 * <li>all possible combinations of ordering schemes and <strong>explicit</strong>
 * base directions, applied to both input and output,</li>
 * <li>selected tests for auto direction (systematically, auto direction is
 * covered in a dedicated test) applied on both input and output,</li>
 * <li>all possible combinations of mirroring, digits and letters applied
 * to output only.</li>
 * </ul>
 */
static void
testAllTransformOptions(void)
{
    static const char *inText =
            "a[b]c \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 23\\u0660 e\\u06314 f \\ufeaf \\u0661\\u0662";

    static const UBidiTestCases testCases[] = {
        { UBIDI_LTR, UBIDI_LOGICAL, UBIDI_LTR, UBIDI_LOGICAL,
            "a[b]c \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 23\\u0660 e\\u06314 f \\ufeaf \\u0661\\u0662", // reordering no mirroring
            "a[b]c \\u05d0)\\u05d1\\u05d2 \\u05d3(\\u05d4 1 d \\u0630 23\\u0660 e\\u06314 f \\ufeaf \\u0661\\u0662", // mirroring
            "a[b]c \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 \\u0662\\u0663\\u0660 e\\u0631\\u0664 f \\ufeaf \\u0661\\u0662", // context numeric shaping
            "1: Logical LTR ==> Logical LTR" },
        { UBIDI_LTR, UBIDI_LOGICAL, UBIDI_LTR, UBIDI_VISUAL,
            "a[b]c 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 d 23\\u0660 \\u0630 e4\\u0631 f \\u0661\\u0662 \\ufeaf",
            "a[b]c 1 \\u05d4(\\u05d3 \\u05d2\\u05d1)\\u05d0 d 23\\u0660 \\u0630 e4\\u0631 f \\u0661\\u0662 \\ufeaf",
            "a[b]c 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 d \\u0662\\u0663\\u0660 \\u0630 e\\u0664\\u0631 f \\u0661\\u0662 \\ufeaf",
            "2: Logical LTR ==> Visual LTR" },
        { UBIDI_LTR, UBIDI_LOGICAL, UBIDI_RTL, UBIDI_LOGICAL,
            "\\ufeaf \\u0661\\u0662 f \\u0631e4 \\u0630 23\\u0660 d \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 a[b]c",
            "\\ufeaf \\u0661\\u0662 f \\u0631e4 \\u0630 23\\u0660 d \\u05d0)\\u05d1\\u05d2 \\u05d3(\\u05d4 1 a[b]c",
            "\\ufeaf \\u0661\\u0662 f \\u0631e\\u0664 \\u0630 \\u0662\\u0663\\u0660 d \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 a[b]c",
            "3: Logical LTR ==> Logical RTL" },
        { UBIDI_LTR, UBIDI_LOGICAL, UBIDI_RTL, UBIDI_VISUAL,
            "\\ufeaf \\u0662\\u0661 f \\u06314e \\u0630 \\u066032 d \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 c]b[a",
            "\\ufeaf \\u0662\\u0661 f \\u06314e \\u0630 \\u066032 d \\u05d0)\\u05d1\\u05d2 \\u05d3(\\u05d4 1 c]b[a",
            "\\ufeaf \\u0662\\u0661 f \\u0631\\u0664e \\u0630 \\u0660\\u0663\\u0662 d \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 c]b[a",
            "4: Logical LTR ==> Visual RTL" },
        { UBIDI_RTL, UBIDI_LOGICAL, UBIDI_RTL, UBIDI_LOGICAL,
            "a[b]c \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 23\\u0660 e\\u06314 f \\ufeaf \\u0661\\u0662",
            "a[b]c \\u05d0)\\u05d1\\u05d2 \\u05d3(\\u05d4 1 d \\u0630 23\\u0660 e\\u06314 f \\ufeaf \\u0661\\u0662", // mirroring
            "a[b]c \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 23\\u0660 e\\u06314 f \\ufeaf \\u0661\\u0662",
            "5: Logical RTL ==> Logical RTL" },
        { UBIDI_RTL, UBIDI_LOGICAL, UBIDI_RTL, UBIDI_VISUAL,
            "c]b[a \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 \\u066032 e\\u06314 f \\ufeaf \\u0662\\u0661",
            "c]b[a \\u05d0)\\u05d1\\u05d2 \\u05d3(\\u05d4 1 d \\u0630 \\u066032 e\\u06314 f \\ufeaf \\u0662\\u0661",
            "c]b[a \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 \\u066032 e\\u06314 f \\ufeaf \\u0662\\u0661",
            "6: Logical RTL ==> Visual RTL" },
        { UBIDI_RTL, UBIDI_LOGICAL, UBIDI_LTR, UBIDI_LOGICAL,
            "\\ufeaf \\u0661\\u0662 f 4\\u0631e 23\\u0630 \\u0660 d 1 \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 a[b]c",
            "\\ufeaf \\u0661\\u0662 f 4\\u0631e 23\\u0630 \\u0660 d 1 \\u05d0)\\u05d1\\u05d2 \\u05d3(\\u05d4 a[b]c",
            "\\ufeaf \\u0661\\u0662 f 4\\u0631e 23\\u0630 \\u0660 d 1 \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 a[b]c",
            "7: Logical RTL ==> Logical LTR" },
        { UBIDI_RTL, UBIDI_LOGICAL, UBIDI_LTR, UBIDI_VISUAL,
            "\\u0661\\u0662 \\ufeaf f 4\\u0631e 23\\u0660 \\u0630 d 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 a[b]c",
            "\\u0661\\u0662 \\ufeaf f 4\\u0631e 23\\u0660 \\u0630 d 1 \\u05d4(\\u05d3 \\u05d2\\u05d1)\\u05d0 a[b]c",
            "\\u0661\\u0662 \\ufeaf f 4\\u0631e 23\\u0660 \\u0630 d 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 a[b]c",
            "8: Logical RTL ==> Visual LTR" },
        { UBIDI_LTR, UBIDI_VISUAL, UBIDI_LTR, UBIDI_VISUAL, 
            "a[b]c \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 23\\u0660 e\\u06314 f \\ufeaf \\u0661\\u0662",
            "a[b]c \\u05d0)\\u05d1\\u05d2 \\u05d3(\\u05d4 1 d \\u0630 23\\u0660 e\\u06314 f \\ufeaf \\u0661\\u0662", // mirroring
            "a[b]c \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 \\u0662\\u0663\\u0660 e\\u0631\\u0664 f \\ufeaf \\u0661\\u0662",
            "9: Visual LTR ==> Visual LTR" },
        { UBIDI_LTR, UBIDI_VISUAL, UBIDI_LTR, UBIDI_LOGICAL,
            "a[b]c 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 d 23\\u0660 \\u0630 e4\\u0631 f \\u0661\\u0662 \\ufeaf",
            "a[b]c 1 \\u05d4(\\u05d3 \\u05d2\\u05d1)\\u05d0 d 23\\u0660 \\u0630 e4\\u0631 f \\u0661\\u0662 \\ufeaf",
            "a[b]c 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 d 23\\u0660 \\u0630 e4\\u0631 f \\u0661\\u0662 \\ufeaf",
            "10: Visual LTR ==> Logical LTR" },
        { UBIDI_LTR, UBIDI_VISUAL, UBIDI_RTL, UBIDI_VISUAL,
            "\\u0662\\u0661 \\ufeaf f 4\\u0631e \\u066032 \\u0630 d 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 c]b[a",
            "\\u0662\\u0661 \\ufeaf f 4\\u0631e \\u066032 \\u0630 d 1 \\u05d4(\\u05d3 \\u05d2\\u05d1)\\u05d0 c]b[a",
            "\\u0662\\u0661 \\ufeaf f \\u0664\\u0631e \\u0660\\u0663\\u0662 \\u0630 d 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 c]b[a",
            "11: Visual LTR ==> Visual RTL" },
        { UBIDI_LTR, UBIDI_VISUAL, UBIDI_RTL, UBIDI_LOGICAL,
            "\\u0661\\u0662 \\ufeaf f 4\\u0631e 23\\u0660 \\u0630 d 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 a[b]c",
            "\\u0661\\u0662 \\ufeaf f 4\\u0631e 23\\u0660 \\u0630 d 1 \\u05d4(\\u05d3 \\u05d2\\u05d1)\\u05d0 a[b]c",
            "\\u0661\\u0662 \\ufeaf f \\u0664\\u0631e \\u0662\\u0663\\u0660 \\u0630 d 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 a[b]c",
            "12: Visual LTR ==> Logical RTL" }, 
        { UBIDI_RTL, UBIDI_VISUAL, UBIDI_RTL, UBIDI_VISUAL,
            "a[b]c \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 23\\u0660 e\\u06314 f \\ufeaf \\u0661\\u0662",
            "a[b]c \\u05d0)\\u05d1\\u05d2 \\u05d3(\\u05d4 1 d \\u0630 23\\u0660 e\\u06314 f \\ufeaf \\u0661\\u0662",
            "a[b]c \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 23\\u0660 e\\u06314 f \\ufeaf \\u0661\\u0662",
            "13: Visual RTL ==> Visual RTL" },
        { UBIDI_RTL, UBIDI_VISUAL, UBIDI_RTL, UBIDI_LOGICAL,
            "c]b[a \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 \\u066032 e\\u06314 f \\ufeaf \\u0662\\u0661",
            "c]b[a \\u05d0)\\u05d1\\u05d2 \\u05d3(\\u05d4 1 d \\u0630 \\u066032 e\\u06314 f \\ufeaf \\u0662\\u0661",
            "c]b[a \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 \\u066032 e\\u06314 f \\ufeaf \\u0662\\u0661",
            "14: Visual RTL ==> Logical RTL" },
        { UBIDI_RTL, UBIDI_VISUAL, UBIDI_LTR, UBIDI_VISUAL,
            "\\u0662\\u0661 \\ufeaf f 4\\u0631e \\u066032 \\u0630 d 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 c]b[a",
            "\\u0662\\u0661 \\ufeaf f 4\\u0631e \\u066032 \\u0630 d 1 \\u05d4(\\u05d3 \\u05d2\\u05d1)\\u05d0 c]b[a",
            "\\u0662\\u0661 \\ufeaf f 4\\u0631e \\u066032 \\u0630 d 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 c]b[a",
            "15: Visual RTL ==> Visual LTR" },
        { UBIDI_RTL, UBIDI_VISUAL, UBIDI_LTR, UBIDI_LOGICAL,
            "\\ufeaf \\u0662\\u0661 f 4\\u0631e \\u066032 \\u0630 d 1 \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 c]b[a",
            "\\ufeaf \\u0662\\u0661 f 4\\u0631e \\u066032 \\u0630 d 1 \\u05d0)\\u05d1\\u05d2 \\u05d3(\\u05d4 c]b[a",
            "\\ufeaf \\u0662\\u0661 f 4\\u0631e \\u066032 \\u0630 d 1 \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 c]b[a",
            "16: Visual RTL ==> Logical LTR" },
        { UBIDI_DEFAULT_RTL, UBIDI_LOGICAL, UBIDI_LTR, UBIDI_VISUAL,
            "a[b]c 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 d 23\\u0660 \\u0630 e4\\u0631 f \\u0661\\u0662 \\ufeaf",
            "a[b]c 1 \\u05d4(\\u05d3 \\u05d2\\u05d1)\\u05d0 d 23\\u0660 \\u0630 e4\\u0631 f \\u0661\\u0662 \\ufeaf",
            "a[b]c 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 d \\u0662\\u0663\\u0660 \\u0630 e\\u0664\\u0631 f \\u0661\\u0662 \\ufeaf",
            "17: Logical DEFAULT_RTL ==> Visual LTR" },
        { UBIDI_RTL, UBIDI_LOGICAL, UBIDI_DEFAULT_LTR, UBIDI_VISUAL,
            "c]b[a \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 \\u066032 e\\u06314 f \\ufeaf \\u0662\\u0661",
            "c]b[a \\u05d0)\\u05d1\\u05d2 \\u05d3(\\u05d4 1 d \\u0630 \\u066032 e\\u06314 f \\ufeaf \\u0662\\u0661",
            "c]b[a \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 \\u066032 e\\u06314 f \\ufeaf \\u0662\\u0661",
            "18: Logical RTL ==> Visual DEFAULT_LTR" },
        { UBIDI_DEFAULT_LTR, UBIDI_LOGICAL, UBIDI_LTR, UBIDI_VISUAL,
            "a[b]c 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 d 23\\u0660 \\u0630 e4\\u0631 f \\u0661\\u0662 \\ufeaf",
            "a[b]c 1 \\u05d4(\\u05d3 \\u05d2\\u05d1)\\u05d0 d 23\\u0660 \\u0630 e4\\u0631 f \\u0661\\u0662 \\ufeaf",
            "a[b]c 1 \\u05d4)\\u05d3 \\u05d2\\u05d1(\\u05d0 d \\u0662\\u0663\\u0660 \\u0630 e\\u0664\\u0631 f \\u0661\\u0662 \\ufeaf",
            "19: Logical DEFAULT_LTR ==> Visual LTR" },
        { UBIDI_RTL, UBIDI_LOGICAL, UBIDI_DEFAULT_RTL, UBIDI_VISUAL,
            "c]b[a \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 \\u066032 e\\u06314 f \\ufeaf \\u0662\\u0661",
            "c]b[a \\u05d0)\\u05d1\\u05d2 \\u05d3(\\u05d4 1 d \\u0630 \\u066032 e\\u06314 f \\ufeaf \\u0662\\u0661",
            "c]b[a \\u05d0(\\u05d1\\u05d2 \\u05d3)\\u05d4 1 d \\u0630 \\u066032 e\\u06314 f \\ufeaf \\u0662\\u0661",
            "20: Logical RTL ==> Visual DEFAULT_RTL" }
    };
    static const uint32_t digits[] = {
        U_SHAPE_DIGITS_NOOP,
        U_SHAPE_DIGITS_AN2EN,
        U_SHAPE_DIGITS_EN2AN,
        U_SHAPE_DIGITS_ALEN2AN_INIT_LR
    };
    static const uint32_t letters[] = {
        U_SHAPE_LETTERS_UNSHAPE,
        U_SHAPE_LETTERS_SHAPE
    };
    const char *expectedStr;
    uint32_t i, nTestCases = sizeof(testCases) / sizeof(testCases[0]);
    uint32_t j, nDigits = sizeof(digits) / sizeof(digits[0]);
    uint32_t k, nLetters = sizeof(letters) / sizeof(letters[0]);
    
    UErrorCode errorCode = U_ZERO_ERROR;
    UBiDiTransform *pTransform = ubiditransform_open(&errorCode);

    u_unescape(inText, src, STR_CAPACITY);

    // Test various combinations of base levels, orders, mirroring, digits and letters
    for (i = 0; i < nTestCases; i++) {
        expectedStr = testCases[i].pReorderAndMirror;
        ubiditransform_transform(pTransform, src, -1, dest, STR_CAPACITY,
                testCases[i].inLevel, testCases[i].inOr,
                testCases[i].outLevel, testCases[i].outOr,
                UBIDI_MIRRORING_ON, 0, &errorCode);
        verifyResultsForAllOpt(&testCases[i], src, dest, expectedStr, U_SHAPE_DIGITS_NOOP,
                U_SHAPE_LETTERS_NOOP);

        for (j = 0; j < nDigits; j++) {
            expectedStr = digits[j] == U_SHAPE_DIGITS_ALEN2AN_INIT_LR ? testCases[i].pContextShapes
                    : testCases[i].pReorderNoMirror;
            for (k = 0; k < nLetters; k++) {
                /* Use here NULL for pTransform */
                ubiditransform_transform(NULL, src, -1, dest, STR_CAPACITY,
                        testCases[i].inLevel, testCases[i].inOr,
                        testCases[i].outLevel, testCases[i].outOr,
                        UBIDI_MIRRORING_OFF, digits[j] | letters[k],
                        &errorCode);
                verifyResultsForAllOpt(&testCases[i], src, dest, expectedStr, digits[j],
                        letters[k]);
            }
        }
    }
    ubiditransform_close(pTransform);
}

void
addBidiTransformTest(TestNode** root)
{
    addTest(root, testAutoDirection, "complex/bidi-transform/TestAutoDirection");
    addTest(root, testAllTransformOptions, "complex/bidi-transform/TestAllTransformOptions");
}

#ifdef __cplusplus
}
#endif