Home | History | Annotate | Download | only in shaping
      1 //  2017 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html#License
      3 
      4 package com.ibm.icu.dev.test.shaping;
      5 
      6 import java.util.Arrays;
      7 import java.util.Collection;
      8 import java.util.MissingResourceException;
      9 
     10 import org.junit.Test;
     11 import org.junit.experimental.runners.Enclosed;
     12 import org.junit.runner.RunWith;
     13 import org.junit.runners.Parameterized;
     14 
     15 import com.ibm.icu.dev.test.TestFmwk;
     16 import com.ibm.icu.text.ArabicShaping;
     17 import com.ibm.icu.text.ArabicShapingException;
     18 
     19 /**
     20  * Regression test for Arabic shaping.
     21  */
     22 @RunWith(Enclosed.class)
     23 public class DataDrivenArabicShapingRegTest extends TestFmwk {
     24 
     25     /* constants copied from ArabicShaping for convenience */
     26 
     27     public static final int LENGTH_GROW_SHRINK = 0;
     28     public static final int LENGTH_FIXED_SPACES_NEAR = 1;
     29     public static final int LENGTH_FIXED_SPACES_AT_END = 2;
     30     public static final int LENGTH_FIXED_SPACES_AT_BEGINNING = 3;
     31 
     32     public static final int TEXT_DIRECTION_LOGICAL = 0;
     33     public static final int TEXT_DIRECTION_VISUAL_LTR = 4;
     34 
     35     public static final int LETTERS_NOOP = 0;
     36     public static final int LETTERS_SHAPE = 8;
     37     public static final int LETTERS_SHAPE_TASHKEEL_ISOLATED = 0x18;
     38     public static final int LETTERS_UNSHAPE = 0x10;
     39 
     40     public static final int DIGITS_NOOP = 0;
     41     public static final int DIGITS_EN2AN = 0x20;
     42     public static final int DIGITS_AN2EN = 0x40;
     43     public static final int DIGITS_EN2AN_INIT_LR = 0x60;
     44     public static final int DIGITS_EN2AN_INIT_AL = 0x80;
     45     //    private static final int DIGITS_RESERVED = 0xa0;
     46 
     47     public static final int DIGIT_TYPE_AN = 0;
     48     public static final int DIGIT_TYPE_AN_EXTENDED = 0x100;
     49 
     50     @RunWith(Parameterized.class)
     51     public static class StandardDataTest extends TestFmwk {
     52         private String source;
     53         private int flags;
     54         private String expected;
     55 
     56         public StandardDataTest(String source, int flags, String expected) {
     57             this.source = source;
     58             this.flags = flags;
     59             this.expected = expected;
     60         }
     61 
     62         @Parameterized.Parameters
     63         public static Collection testData() {
     64             String lamAlefSpecialVLTR =
     65                 "\u0020\u0646\u0622\u0644\u0627\u0020\u0646\u0623\u064E\u0644\u0627\u0020" +
     66                 "\u0646\u0627\u0670\u0644\u0627\u0020\u0646\u0622\u0653\u0644\u0627\u0020" +
     67                 "\u0646\u0625\u0655\u0644\u0627\u0020\u0646\u0622\u0654\u0644\u0627\u0020" +
     68                 "\uFEFC\u0639";
     69             String tashkeelSpecialVLTR =
     70                 "\u064A\u0628\u0631\u0639\u0020\u064A\u0628\u0651\u0631\u064E\u0639\u0020" +
     71                 "\u064C\u064A\u0628\u0631\u064F\u0639\u0020\u0628\u0670\u0631\u0670\u0639" +
     72                 "\u0020\u0628\u0653\u0631\u0653\u0639\u0020\u0628\u0654\u0631\u0654\u0639" +
     73                 "\u0020\u0628\u0655\u0631\u0655\u0639\u0020";
     74             String tashkeelShaddaRTL=
     75                 "\u0634\u0651\u0645\u0652\u0633";
     76             String tashkeelShaddaLTR=
     77                 "\u0633\u0652\u0645\u0651\u0634";
     78             String ArMathSym =
     79                 "\uD83B\uDE00\uD83B\uDE01\uD83B\uDE02\uD83B\uDE03\u0020\uD83B\uDE24\uD83B" +
     80                 "\uDE05\uD83B\uDE06\u0020\uD83B\uDE07\uD83B\uDE08\uD83B\uDE09\u0020\uD83B" +
     81                 "\uDE0A\uD83B\uDE0B\uD83B\uDE0C\uD83B\uDE0D\u0020\uD83B\uDE0E\uD83B\uDE0F" +
     82                 "\uD83B\uDE10\uD83B\uDE11\u0020\uD83B\uDE12\uD83B\uDE13\uD83B\uDE14\uD83B" +
     83                 "\uDE15\u0020\uD83B\uDE16\uD83B\uDE17\uD83B\uDE18\u0020\uD83B\uDE19\uD83B" +
     84                 "\uDE1A\uD83B\uDE1B";
     85             String ArMathSymLooped =
     86                 "\uD83B\uDE80\uD83B\uDE81\uD83B\uDE82\uD83B\uDE83\u0020\uD83B\uDE84\uD83B" +
     87                 "\uDE85\uD83B\uDE86\u0020\uD83B\uDE87\uD83B\uDE88\uD83B\uDE89\u0020\uD83B" +
     88                 "\uDE8B\uD83B\uDE8C\uD83B\uDE8D\u0020\uD83B\uDE8E\uD83B\uDE8F\uD83B\uDE90" +
     89                 "\uD83B\uDE91\u0020\uD83B\uDE92\uD83B\uDE93\uD83B\uDE94\uD83B\uDE95\u0020" +
     90                 "\uD83B\uDE96\uD83B\uDE97\uD83B\uDE98\u0020\uD83B\uDE99\uD83B\uDE9A\uD83B" +
     91                 "\uDE9B";
     92             String ArMathSymDoubleStruck =
     93                 "\uD83B\uDEA1\uD83B\uDEA2\uD83B\uDEA3\u0020\uD83B\uDEA5\uD83B\uDEA6\u0020" +
     94                 "\uD83B\uDEA7\uD83B\uDEA8\uD83B\uDEA9\u0020\uD83B\uDEAB\uD83B\uDEAC\uD83B" +
     95                 "\uDEAD\u0020\uD83B\uDEAE\uD83B\uDEAF\uD83B\uDEB0\uD83B\uDEB1\u0020\uD83B" +
     96                 "\uDEB2\uD83B\uDEB3\uD83B\uDEB4\uD83B\uDEB5\u0020\uD83B\uDEB6\uD83B\uDEB7" +
     97                 "\uD83B\uDEB8\u0020\uD83B\uDEB9\uD83B\uDEBA\uD83B\uDEBB";
     98             String ArMathSymInitial =
     99                 "\uD83B\uDE21\uD83B\uDE22\u0020\uD83B\uDE27\uD83B\uDE29\u0020\uD83B\uDE2A" +
    100                 "\uD83B\uDE2B\uD83B\uDE2C\uD83B\uDE2D\u0020\uD83B\uDE2E\uD83B\uDE2F\uD83B" +
    101                 "\uDE30\uD83B\uDE31\u0020\uD83B\uDE32\uD83B\uDE34\uD83B\uDE35\u0020\uD83B" +
    102                 "\uDE36\uD83B\uDE37\u0020\uD83B\uDE39\uD83B\uDE3B";
    103             String ArMathSymTailed =
    104                 "\uD83B\uDE42\uD83B\uDE47\uD83B\uDE49\uD83B\uDE4B\u0020\uD83B\uDE4D\uD83B" +
    105                 "\uDE4E\uD83B\uDE4F\u0020\uD83B\uDE51\uD83B\uDE52\uD83B\uDE54\uD83B\uDE57" +
    106                 "\u0020\uD83B\uDE59\uD83B\uDE5B\uD83B\uDE5D\uD83B\uDE5F";
    107             String ArMathSymStretched =
    108                 "\uD83B\uDE21\u0633\uD83B\uDE62\u0647";
    109             String logicalUnshape =
    110                 "\u0020\u0020\u0020\uFE8D\uFEF5\u0020\uFEE5\u0020\uFE8D\uFEF7\u0020\uFED7" +
    111                 "\uFEFC\u0020\uFEE1\u0020\uFE8D\uFEDF\uFECC\uFEAE\uFE91\uFEF4\uFE94\u0020" +
    112                 "\uFE8D\uFEDF\uFEA4\uFEAE\uFE93\u0020\u0020\u0020\u0020";
    113             String numSource =
    114                 "\u0031" +  /* en:1 */
    115                 "\u0627" +  /* arabic:alef */
    116                 "\u0032" +  /* en:2 */
    117                 "\u06f3" +  /* an:3 */
    118                 "\u0061" +  /* latin:a */
    119                 "\u0034";   /* en:4 */
    120 
    121             return Arrays.asList(new Object[][] {
    122                     /* lam alef special visual ltr */
    123                     {lamAlefSpecialVLTR,
    124                      LETTERS_SHAPE | TEXT_DIRECTION_VISUAL_LTR | LENGTH_FIXED_SPACES_NEAR,
    125                      "\u0020\ufee5\u0020\ufef5\ufe8d\u0020\ufee5\u0020\ufe76\ufef7\ufe8d\u0020" +
    126                      "\ufee5\u0020\u0670\ufefb\ufe8d\u0020\ufee5\u0020\u0653\ufef5\ufe8d\u0020" +
    127                      "\ufee5\u0020\u0655\ufef9\ufe8d\u0020\ufee5\u0020\u0654\ufef5\ufe8d\u0020" +
    128                      "\ufefc\ufecb"},
    129                     {lamAlefSpecialVLTR,
    130                      LETTERS_SHAPE | TEXT_DIRECTION_VISUAL_LTR | LENGTH_FIXED_SPACES_AT_END,
    131                      "\u0020\ufee5\ufef5\ufe8d\u0020\ufee5\ufe76\ufef7\ufe8d\u0020\ufee5\u0670" +
    132                      "\ufefb\ufe8d\u0020\ufee5\u0653\ufef5\ufe8d\u0020\ufee5\u0655\ufef9\ufe8d" +
    133                      "\u0020\ufee5\u0654\ufef5\ufe8d\u0020\ufefc\ufecb\u0020\u0020\u0020\u0020" +
    134                      "\u0020\u0020"},
    135                     {lamAlefSpecialVLTR,
    136                      LETTERS_SHAPE | TEXT_DIRECTION_VISUAL_LTR | LENGTH_FIXED_SPACES_AT_BEGINNING,
    137                      "\u0020\u0020\u0020\u0020\u0020\u0020\u0020\ufee5\ufef5\ufe8d\u0020\ufee5" +
    138                      "\ufe76\ufef7\ufe8d\u0020\ufee5\u0670\ufefb\ufe8d\u0020\ufee5\u0653\ufef5" +
    139                      "\ufe8d\u0020\ufee5\u0655\ufef9\ufe8d\u0020\ufee5\u0654\ufef5\ufe8d\u0020" +
    140                      "\ufefc\ufecb"},
    141                     {lamAlefSpecialVLTR,
    142                      LETTERS_SHAPE | TEXT_DIRECTION_VISUAL_LTR | LENGTH_GROW_SHRINK,
    143                      "\u0020\ufee5\ufef5\ufe8d\u0020\ufee5\ufe76\ufef7\ufe8d\u0020\ufee5\u0670" +
    144                      "\ufefb\ufe8d\u0020\ufee5\u0653\ufef5\ufe8d\u0020\ufee5\u0655\ufef9\ufe8d" +
    145                      "\u0020\ufee5\u0654\ufef5\ufe8d\u0020\ufefc\ufecb"},
    146                     /* TASHKEEL */
    147                     {lamAlefSpecialVLTR,
    148                      LETTERS_SHAPE_TASHKEEL_ISOLATED | TEXT_DIRECTION_VISUAL_LTR |
    149                      LENGTH_FIXED_SPACES_NEAR,
    150                      "\u0020\ufee5\u0020\ufef5\ufe8d\u0020\ufee5\u0020\ufe76\ufef7\ufe8d\u0020" +
    151                      "\ufee5\u0020\u0670\ufefb\ufe8d\u0020\ufee5\u0020\u0653\ufef5\ufe8d\u0020" +
    152                      "\ufee5\u0020\u0655\ufef9\ufe8d\u0020\ufee5\u0020\u0654\ufef5\ufe8d\u0020" +
    153                      "\ufefc\ufecb"},
    154                     {lamAlefSpecialVLTR,
    155                      LETTERS_SHAPE_TASHKEEL_ISOLATED | TEXT_DIRECTION_VISUAL_LTR |
    156                      LENGTH_FIXED_SPACES_AT_END,
    157                      "\u0020\ufee5\ufef5\ufe8d\u0020\ufee5\ufe76\ufef7\ufe8d\u0020\ufee5\u0670" +
    158                      "\ufefb\ufe8d\u0020\ufee5\u0653\ufef5\ufe8d\u0020\ufee5\u0655\ufef9\ufe8d" +
    159                      "\u0020\ufee5\u0654\ufef5\ufe8d\u0020\ufefc\ufecb\u0020\u0020\u0020\u0020" +
    160                      "\u0020\u0020"},
    161                     {lamAlefSpecialVLTR,
    162                      LETTERS_SHAPE_TASHKEEL_ISOLATED | TEXT_DIRECTION_VISUAL_LTR |
    163                      LENGTH_FIXED_SPACES_AT_BEGINNING,
    164                      "\u0020\u0020\u0020\u0020\u0020\u0020\u0020\ufee5\ufef5\ufe8d\u0020\ufee5" +
    165                      "\ufe76\ufef7\ufe8d\u0020\ufee5\u0670\ufefb\ufe8d\u0020\ufee5\u0653\ufef5" +
    166                      "\ufe8d\u0020\ufee5\u0655\ufef9\ufe8d\u0020\ufee5\u0654\ufef5\ufe8d\u0020" +
    167                      "\ufefc\ufecb"},
    168                     {lamAlefSpecialVLTR,
    169                      LETTERS_SHAPE_TASHKEEL_ISOLATED | TEXT_DIRECTION_VISUAL_LTR |
    170                      LENGTH_GROW_SHRINK,
    171                      "\u0020\ufee5\ufef5\ufe8d\u0020\ufee5\ufe76\ufef7\ufe8d\u0020\ufee5\u0670" +
    172                      "\ufefb\ufe8d\u0020\ufee5\u0653\ufef5\ufe8d\u0020\ufee5\u0655\ufef9\ufe8d" +
    173                      "\u0020\ufee5\u0654\ufef5\ufe8d\u0020\ufefc\ufecb"},
    174                     /* tashkeel special visual ltr */
    175                     {tashkeelSpecialVLTR,
    176                      LETTERS_SHAPE | TEXT_DIRECTION_VISUAL_LTR | LENGTH_FIXED_SPACES_NEAR,
    177                      "\ufef2\ufe91\ufeae\ufecb\u0020\ufef2\ufe91\ufe7c\ufeae\ufe77\ufecb\u0020" +
    178                      "\ufe72\ufef2\ufe91\ufeae\ufe79\ufecb\u0020\ufe8f\u0670\ufeae\u0670\ufecb" +
    179                      "\u0020\ufe8f\u0653\ufeae\u0653\ufecb\u0020\ufe8f\u0654\ufeae\u0654\ufecb" +
    180                      "\u0020\ufe8f\u0655\ufeae\u0655\ufecb\u0020"},
    181                     {tashkeelSpecialVLTR,
    182                      LETTERS_SHAPE_TASHKEEL_ISOLATED | TEXT_DIRECTION_VISUAL_LTR |
    183                      LENGTH_FIXED_SPACES_NEAR,
    184                      "\ufef2\ufe91\ufeae\ufecb\u0020\ufef2\ufe91\ufe7c\ufeae\ufe76\ufecb\u0020" +
    185                      "\ufe72\ufef2\ufe91\ufeae\ufe78\ufecb\u0020\ufe8f\u0670\ufeae\u0670\ufecb" +
    186                      "\u0020\ufe8f\u0653\ufeae\u0653\ufecb\u0020\ufe8f\u0654\ufeae\u0654\ufecb" +
    187                      "\u0020\ufe8f\u0655\ufeae\u0655\ufecb\u0020"},
    188                     {tashkeelShaddaRTL,
    189                      ArabicShaping.LETTERS_SHAPE | ArabicShaping.TASHKEEL_BEGIN |
    190                      ArabicShaping.TEXT_DIRECTION_VISUAL_RTL,
    191                      "\u0020\ufeb7\ufe7d\ufee4\ufeb2"},
    192                     {tashkeelShaddaRTL,
    193                      ArabicShaping.LETTERS_SHAPE | ArabicShaping.TASHKEEL_END |
    194                      ArabicShaping.TEXT_DIRECTION_VISUAL_RTL,
    195                      "\ufeb7\ufe7d\ufee4\ufeb2\u0020"},
    196                     {tashkeelShaddaRTL,
    197                      ArabicShaping.LETTERS_SHAPE | ArabicShaping.TASHKEEL_RESIZE |
    198                      ArabicShaping.TEXT_DIRECTION_VISUAL_RTL,
    199                      "\ufeb7\ufe7d\ufee4\ufeb2"},
    200                     {tashkeelShaddaRTL,
    201                      ArabicShaping.LETTERS_SHAPE | ArabicShaping.TASHKEEL_REPLACE_BY_TATWEEL |
    202                      ArabicShaping.TEXT_DIRECTION_VISUAL_RTL,
    203                      "\ufeb7\ufe7d\ufee4\u0640\ufeb2"},
    204                     {tashkeelShaddaLTR,
    205                      ArabicShaping.LETTERS_SHAPE | ArabicShaping.TASHKEEL_BEGIN |
    206                      ArabicShaping.TEXT_DIRECTION_VISUAL_LTR,
    207                      "\u0020\ufeb2\ufee4\ufe7d\ufeb7"},
    208                     {tashkeelShaddaLTR,
    209                      ArabicShaping.LETTERS_SHAPE | ArabicShaping.TASHKEEL_END |
    210                      ArabicShaping.TEXT_DIRECTION_VISUAL_LTR,
    211                      "\ufeb2\ufee4\ufe7d\ufeb7\u0020"},
    212                     {tashkeelShaddaLTR,
    213                      ArabicShaping.LETTERS_SHAPE | ArabicShaping.TASHKEEL_RESIZE |
    214                      ArabicShaping.TEXT_DIRECTION_VISUAL_LTR,
    215                      "\ufeb2\ufee4\ufe7d\ufeb7"},
    216                     {tashkeelShaddaLTR,
    217                      ArabicShaping.LETTERS_SHAPE | ArabicShaping.TASHKEEL_REPLACE_BY_TATWEEL |
    218                      ArabicShaping.TEXT_DIRECTION_VISUAL_LTR,
    219                      "\ufeb2\u0640\ufee4\ufe7d\ufeb7"},
    220                     {ArMathSym,
    221                      ArabicShaping.LETTERS_SHAPE | ArabicShaping.TASHKEEL_BEGIN |
    222                      ArabicShaping.TEXT_DIRECTION_VISUAL_RTL,
    223                      "\uD83B\uDE00\uD83B\uDE01\uD83B\uDE02\uD83B\uDE03\u0020\uD83B\uDE24\uD83B" +
    224                      "\uDE05\uD83B\uDE06\u0020\uD83B\uDE07\uD83B\uDE08\uD83B\uDE09\u0020\uD83B" +
    225                      "\uDE0A\uD83B\uDE0B\uD83B\uDE0C\uD83B\uDE0D\u0020\uD83B\uDE0E\uD83B\uDE0F" +
    226                      "\uD83B\uDE10\uD83B\uDE11\u0020\uD83B\uDE12\uD83B\uDE13\uD83B\uDE14\uD83B" +
    227                      "\uDE15\u0020\uD83B\uDE16\uD83B\uDE17\uD83B\uDE18\u0020\uD83B\uDE19\uD83B" +
    228                      "\uDE1A\uD83B\uDE1B"},
    229                     {ArMathSymLooped,
    230                      ArabicShaping.LETTERS_SHAPE | ArabicShaping.TASHKEEL_END |
    231                      ArabicShaping.TEXT_DIRECTION_VISUAL_RTL,
    232                      "\uD83B\uDE80\uD83B\uDE81\uD83B\uDE82\uD83B\uDE83\u0020\uD83B\uDE84\uD83B" +
    233                      "\uDE85\uD83B\uDE86\u0020\uD83B\uDE87\uD83B\uDE88\uD83B\uDE89\u0020\uD83B" +
    234                      "\uDE8B\uD83B\uDE8C\uD83B\uDE8D\u0020\uD83B\uDE8E\uD83B\uDE8F\uD83B\uDE90" +
    235                      "\uD83B\uDE91\u0020\uD83B\uDE92\uD83B\uDE93\uD83B\uDE94\uD83B\uDE95\u0020" +
    236                      "\uD83B\uDE96\uD83B\uDE97\uD83B\uDE98\u0020\uD83B\uDE99\uD83B\uDE9A\uD83B" +
    237                      "\uDE9B"},
    238                     {ArMathSymDoubleStruck,
    239                      ArabicShaping.LETTERS_SHAPE | ArabicShaping.TASHKEEL_RESIZE|
    240                      ArabicShaping.TEXT_DIRECTION_VISUAL_RTL,
    241                      "\uD83B\uDEA1\uD83B\uDEA2\uD83B\uDEA3\u0020\uD83B\uDEA5\uD83B\uDEA6\u0020" +
    242                      "\uD83B\uDEA7\uD83B\uDEA8\uD83B\uDEA9\u0020\uD83B\uDEAB\uD83B\uDEAC\uD83B" +
    243                      "\uDEAD\u0020\uD83B\uDEAE\uD83B\uDEAF\uD83B\uDEB0\uD83B\uDEB1\u0020\uD83B" +
    244                      "\uDEB2\uD83B\uDEB3\uD83B\uDEB4\uD83B\uDEB5\u0020\uD83B\uDEB6\uD83B\uDEB7" +
    245                      "\uD83B\uDEB8\u0020\uD83B\uDEB9\uD83B\uDEBA\uD83B\uDEBB"},
    246                     {ArMathSymInitial,
    247                      ArabicShaping.LETTERS_SHAPE | ArabicShaping.TASHKEEL_BEGIN |
    248                      ArabicShaping.TEXT_DIRECTION_VISUAL_LTR,
    249                      "\uD83B\uDE21\uD83B\uDE22\u0020\uD83B\uDE27\uD83B\uDE29\u0020\uD83B\uDE2A" +
    250                      "\uD83B\uDE2B\uD83B\uDE2C\uD83B\uDE2D\u0020\uD83B\uDE2E\uD83B\uDE2F\uD83B" +
    251                      "\uDE30\uD83B\uDE31\u0020\uD83B\uDE32\uD83B\uDE34\uD83B\uDE35\u0020\uD83B" +
    252                      "\uDE36\uD83B\uDE37\u0020\uD83B\uDE39\uD83B\uDE3B"},
    253                     {ArMathSymTailed,
    254                      ArabicShaping.LETTERS_SHAPE | ArabicShaping.TASHKEEL_END |
    255                      ArabicShaping.TEXT_DIRECTION_VISUAL_LTR,
    256                      "\uD83B\uDE42\uD83B\uDE47\uD83B\uDE49\uD83B\uDE4B\u0020\uD83B\uDE4D\uD83B" +
    257                      "\uDE4E\uD83B\uDE4F\u0020\uD83B\uDE51\uD83B\uDE52\uD83B\uDE54\uD83B\uDE57" +
    258                      "\u0020\uD83B\uDE59\uD83B\uDE5B\uD83B\uDE5D\uD83B\uDE5F"},
    259                     {ArMathSymStretched,
    260                      ArabicShaping.LETTERS_SHAPE|ArabicShaping.TASHKEEL_RESIZE |
    261                      ArabicShaping.TEXT_DIRECTION_VISUAL_LTR,
    262                      "\uD83B\uDE21\uFEB1\uD83B\uDE62\uFEE9"},
    263                      /* logical unshape */
    264                     {logicalUnshape,
    265                      LETTERS_UNSHAPE | TEXT_DIRECTION_LOGICAL | LENGTH_FIXED_SPACES_NEAR,
    266                      "\u0020\u0020\u0020\u0627\u0644\u0622\u0646\u0020\u0627\u0644\u0623\u0642" +
    267                      "\u0644\u0627\u0645\u0020\u0627\u0644\u0639\u0631\u0628\u064a\u0629\u0020" +
    268                      "\u0627\u0644\u062d\u0631\u0629\u0020\u0020\u0020\u0020"},
    269                     {logicalUnshape,
    270                      LETTERS_UNSHAPE | TEXT_DIRECTION_LOGICAL | LENGTH_FIXED_SPACES_AT_END,
    271                      "\u0020\u0020\u0020\u0627\u0644\u0622\u0020\u0646\u0020\u0627\u0644\u0623" +
    272                      "\u0020\u0642\u0644\u0627\u0020\u0645\u0020\u0627\u0644\u0639\u0631\u0628" +
    273                      "\u064a\u0629\u0020\u0627\u0644\u062d\u0631\u0629\u0020"},
    274                     {logicalUnshape,
    275                      LETTERS_UNSHAPE | TEXT_DIRECTION_LOGICAL | LENGTH_FIXED_SPACES_AT_BEGINNING,
    276                      "\u0627\u0644\u0622\u0020\u0646\u0020\u0627\u0644\u0623\u0020\u0642\u0644" +
    277                      "\u0627\u0020\u0645\u0020\u0627\u0644\u0639\u0631\u0628\u064a\u0629\u0020" +
    278                      "\u0627\u0644\u062d\u0631\u0629\u0020\u0020\u0020\u0020"},
    279                     {logicalUnshape,
    280                      LETTERS_UNSHAPE | TEXT_DIRECTION_LOGICAL | LENGTH_GROW_SHRINK,
    281                      "\u0020\u0020\u0020\u0627\u0644\u0622\u0020\u0646\u0020\u0627\u0644\u0623" +
    282                      "\u0020\u0642\u0644\u0627\u0020\u0645\u0020\u0627\u0644\u0639\u0631\u0628" +
    283                      "\u064a\u0629\u0020\u0627\u0644\u062d\u0631\u0629\u0020\u0020\u0020\u0020"},
    284                     /* numbers */
    285                     {numSource,
    286                      DIGITS_EN2AN | DIGIT_TYPE_AN,
    287                      "\u0661\u0627\u0662\u06f3\u0061\u0664"},
    288                     {numSource,
    289                      DIGITS_AN2EN | DIGIT_TYPE_AN_EXTENDED,
    290                     "\u0031\u0627\u0032\u0033\u0061\u0034"},
    291                     {numSource,
    292                      DIGITS_EN2AN_INIT_LR | DIGIT_TYPE_AN,
    293                      "\u0031\u0627\u0662\u06f3\u0061\u0034" },
    294                     {numSource,
    295                      DIGITS_EN2AN_INIT_AL | DIGIT_TYPE_AN_EXTENDED,
    296                      "\u06f1\u0627\u06f2\u06f3\u0061\u0034"},
    297                     {numSource,
    298                      DIGITS_EN2AN_INIT_LR | DIGIT_TYPE_AN | TEXT_DIRECTION_VISUAL_LTR,
    299                      "\u0661\u0627\u0032\u06f3\u0061\u0034"},
    300                     {numSource,
    301                      DIGITS_EN2AN_INIT_AL | DIGIT_TYPE_AN_EXTENDED | TEXT_DIRECTION_VISUAL_LTR,
    302                      "\u06f1\u0627\u0032\u06f3\u0061\u06f4"},
    303                     /* no-op */
    304                     {numSource, 0, numSource}
    305                 });
    306         }
    307 
    308         @Test
    309         public void TestStandard() {
    310             Exception ex = null;
    311             String actual = null;
    312             ArabicShaping shaper = null;
    313 
    314             try {
    315                 shaper = new ArabicShaping(flags);
    316                 actual = shaper.shape(source);
    317             }
    318             catch(MissingResourceException e){
    319                 throw e;
    320             }
    321             catch (IllegalStateException ie){
    322                 warnln("IllegalStateException: "+ ie.toString());
    323                 return;
    324             }
    325             catch (Exception e) {
    326                 ex = e;
    327             }
    328 
    329             if (ex != null) {
    330                 err("Error: Shaper " + shaper + "\n throws exception '" + ex + "'\n for input '" + source);
    331             } else if (!expected.equals(actual)) {
    332                 StringBuffer buf = new StringBuffer();
    333                 buf.append("Error: Shaper: " + shaper + "\n Input: " + source + "\n Actual: " + actual +
    334                            "\n Expected: " + expected + "\n");
    335 
    336                 for (int i = 0; i < Math.max(expected.length(), actual.length()); ++i) {
    337                     String temp = Integer.toString(i);
    338                     if (temp.length() < 2) {
    339                         temp = " ".concat(temp);
    340                     }
    341                     char trg = i < expected.length() ? expected.charAt(i) : '\uffff';
    342                     char res = i < actual.length() ? actual.charAt(i) : '\uffff';
    343 
    344                     buf.append("[" + temp + "] ");
    345                     buf.append(escapedString("" + trg) + " ");
    346                     buf.append(escapedString("" + res) + " ");
    347                     if (trg != res) {
    348                         buf.append("***");
    349                     }
    350                     buf.append("\n");
    351                 }
    352                 err(buf.toString());
    353             }
    354         }
    355 
    356         private static String escapedString(String str) {
    357             if (str == null) {
    358                 return null;
    359             }
    360 
    361             StringBuffer buf = new StringBuffer(str.length() * 6);
    362             for (int i = 0; i < str.length(); ++i) {
    363                 char ch = str.charAt(i);
    364                 buf.append("\\u");
    365                 if (ch < 0x1000) {
    366                     buf.append('0');
    367                 }
    368                 if (ch < 0x0100) {
    369                     buf.append('0');
    370                 }
    371                 if (ch < 0x0010) {
    372                     buf.append('0');
    373                 }
    374                 buf.append(Integer.toHexString(ch));
    375             }
    376             return buf.toString();
    377         }
    378     }
    379 
    380     @RunWith(Parameterized.class)
    381     public static class PreflightDataTest extends TestFmwk {
    382         private String source;
    383         private int flags;
    384         private int length;
    385 
    386         public PreflightDataTest(String source, int flags, int length) {
    387             this.source = source;
    388             this.flags = flags;
    389             this.length = length;
    390         }
    391 
    392         @Parameterized.Parameters
    393         public static Collection testData() {
    394             return Arrays.asList(new Object[][] {
    395                     {"\u0644\u0627", LETTERS_SHAPE | LENGTH_GROW_SHRINK, 1},
    396                     {"\u0644\u0627\u0031",
    397                      DIGITS_EN2AN | DIGIT_TYPE_AN_EXTENDED | LENGTH_GROW_SHRINK, 3},
    398                     {"\u0644\u0644", LETTERS_SHAPE | LENGTH_GROW_SHRINK, 2},
    399                     {"\ufef7", LETTERS_UNSHAPE | LENGTH_GROW_SHRINK, 2}
    400                 });
    401         }
    402 
    403         @Test
    404         public void TestPreflight() {
    405             Exception ex = null;
    406             char src[] = null;
    407             int len = 0;
    408             ArabicShaping shaper = null;
    409 
    410             if (source != null) {
    411                 src = source.toCharArray();
    412             }
    413 
    414             try {
    415                 shaper = new ArabicShaping(flags);
    416                 len = shaper.shape(src, 0, src.length, null, 0, 0);
    417             }
    418             catch (Exception e) {
    419                 ex = e;
    420             }
    421 
    422             if (ex != null) {
    423                 err("Error: Shaper " + shaper + "\n throws exception '" + ex + "'\n for input '" + source);
    424             } else if (length != len) {
    425                 err("Error: Shaper " + shaper + "\n returns " + len + " characters for input '" +
    426                     source + "'\n Expected were " + length + " characters");
    427             }
    428         }
    429     }
    430 
    431     @RunWith(Parameterized.class)
    432     public static class ErrorDataTest extends TestFmwk {
    433         private String source;
    434         private int flags;
    435         private Class error;
    436 
    437         public ErrorDataTest(String source, int flags, Class error) {
    438             this.source = source;
    439             this.flags = flags;
    440             this.error = error;
    441         }
    442 
    443         @Parameterized.Parameters
    444         public static Collection testData() {
    445             return Arrays.asList(new Object[][] {
    446                     /* bad data */
    447                     {"\u0020\ufef7\u0644\u0020", LETTERS_UNSHAPE | LENGTH_FIXED_SPACES_NEAR,
    448                      ArabicShapingException.class},
    449                     {"\u0020\ufef7", LETTERS_UNSHAPE | LENGTH_FIXED_SPACES_AT_END,
    450                      ArabicShapingException.class},
    451                     {"\ufef7\u0020", LETTERS_UNSHAPE | LENGTH_FIXED_SPACES_AT_BEGINNING,
    452                      ArabicShapingException.class},
    453                     /* bad options */
    454                     {"\ufef7", 0xffffffff, IllegalArgumentException.class},
    455                     {"\ufef7", LETTERS_UNSHAPE | LENGTH_GROW_SHRINK, ArabicShapingException.class},
    456                     {null, LETTERS_UNSHAPE | LENGTH_FIXED_SPACES_AT_END,
    457                      IllegalArgumentException.class}
    458                 });
    459         }
    460 
    461         @Test
    462         public void TestError() {
    463             Exception ex = null;
    464             char src[] = null;
    465             int len = 0;
    466             ArabicShaping shaper = null;
    467 
    468             if (source != null) {
    469                 src = source.toCharArray();
    470                 len = src.length;
    471             }
    472 
    473             try {
    474                 shaper = new ArabicShaping(flags);
    475                 shaper.shape(src, 0, len);
    476             }
    477             catch (Exception e) {
    478                 ex = e;
    479             }
    480 
    481             if (!error.isInstance(ex)) {
    482                 err("Error: Shaper " + shaper + "\n throws exception '" + ex + "'\n for input '" +
    483                     source + "'\n Expected exception: " + error);
    484             }
    485         }
    486     }
    487 }
    488