Home | History | Annotate | Download | only in util
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html#License
      3 /*
      4  *******************************************************************************
      5  * Copyright (C) 2007, International Business Machines Corporation and         *
      6  * others. All Rights Reserved.                                                *
      7  *******************************************************************************
      8 */
      9 package com.ibm.icu.dev.test.util;
     10 
     11 import java.util.Arrays;
     12 import java.util.Iterator;
     13 
     14 import org.junit.Test;
     15 import org.junit.runner.RunWith;
     16 import org.junit.runners.JUnit4;
     17 
     18 import com.ibm.icu.dev.test.TestFmwk;
     19 import com.ibm.icu.impl.TextTrieMap;
     20 
     21 @RunWith(JUnit4.class)
     22 public class TextTrieMapTest extends TestFmwk {
     23 
     24     private static final Integer SUN = new Integer(1);
     25     private static final Integer MON = new Integer(2);
     26     private static final Integer TUE = new Integer(3);
     27     private static final Integer WED = new Integer(4);
     28     private static final Integer THU = new Integer(5);
     29     private static final Integer FRI = new Integer(6);
     30     private static final Integer SAT = new Integer(7);
     31 
     32     private static final Integer SUP1 = new Integer(8);
     33     private static final Integer SUP2 = new Integer(9);
     34     private static final Integer SUP3 = new Integer(10);
     35     private static final Integer SUP4 = new Integer(11);
     36 
     37     private static final Integer FOO = new Integer(-1);
     38     private static final Integer BAR = new Integer(-2);
     39 
     40     private static final Object[][] TESTDATA = {
     41         {"Sunday", SUN},
     42         {"Monday", MON},
     43         {"Tuesday", TUE},
     44         {"Wednesday", WED},
     45         {"Thursday", THU},
     46         {"Friday", FRI},
     47         {"Saturday", SAT},
     48         {"Sun", SUN},
     49         {"Mon", MON},
     50         {"Tue", TUE},
     51         {"Wed", WED},
     52         {"Thu", THU},
     53         {"Fri", FRI},
     54         {"Sat", SAT},
     55         {"S", SUN},
     56         {"M", MON},
     57         {"T", TUE},
     58         {"W", WED},
     59         {"T", THU},
     60         {"F", FRI},
     61         {"S", SAT},
     62         {"L", SUP1}, // L, 0xD83D, 0xDCFA
     63         {"L1", SUP2}, // L, 0xD83D, 0xDCFA, 1
     64         {"L", SUP3}, // L, 0xD83D, 0xDCFB
     65         {"L", SUP4}, // L, 0xD83C, 0xDCCF
     66     };
     67 
     68     private static final Object[][] TESTCASES = {
     69         {"Sunday", SUN, SUN},
     70         {"sunday", null, SUN},
     71         {"Mo", MON, MON},
     72         {"mo", null, MON},
     73         {"Thursday Friday", THU, THU},
     74         {"T", new Object[]{TUE, THU}, new Object[]{TUE, THU}},
     75         {"TEST", new Object[]{TUE, THU}, new Object[]{TUE, THU}},
     76         {"SUN", new Object[]{SUN, SAT}, SUN},
     77         {"super", null, SUN},
     78         {"NO", null, null},
     79         {"L", SUP1, SUP1},
     80         {"l", null, SUP1},
     81     };
     82 
     83     private static final Object[][] TESTCASES_PARSE = {
     84             {
     85                 "Sunday",
     86                 new Object[]{
     87                         new Object[]{SAT,SUN}, new Object[]{SAT,SUN}, // matches on "S"
     88                         null, null, // matches on "Su"
     89                         SUN, SUN, // matches on "Sun"
     90                         null, null, // matches on "Sund"
     91                         null, null, // matches on "Sunda"
     92                         SUN, SUN, // matches on "Sunday"
     93                 }
     94             },
     95             {
     96                 "sunday",
     97                 new Object[]{
     98                         null, new Object[]{SAT,SUN}, // matches on "s"
     99                         null, null, // matches on "su"
    100                         null, SUN, // matches on "sun"
    101                         null, null, // matches on "sund"
    102                         null, null, // matches on "sunda"
    103                         null, SUN, // matches on "sunday"
    104                 }
    105             },
    106             {
    107                 "MMM",
    108                 new Object[]{
    109                         MON, MON, // matches on "M"
    110                         // no more matches in data
    111                 }
    112             },
    113             {
    114                 "BBB",
    115                 new Object[]{
    116                         // no matches in data
    117                 }
    118             },
    119             {
    120                 "l12",
    121                 new Object[]{
    122                         null, null, // matches on "L"
    123                         null, SUP1, // matches on "L"
    124                         null, SUP2, // matches on "L1"
    125                         // no more matches in data
    126                 }
    127             },
    128             {
    129                 "L",
    130                 new Object[] {
    131                         null, null, // matches on "L"
    132                         SUP3, SUP3, // matches on "L"
    133                 }
    134             },
    135             {
    136                 "L",
    137                 new Object[] {
    138                         null, null, // matches on "L"
    139                         SUP4, SUP4, // matches on "L"
    140                 }
    141             }
    142     };
    143 
    144     @Test
    145     public void TestCaseSensitive() {
    146         Iterator itr = null;
    147         TextTrieMap map = new TextTrieMap(false);
    148         for (int i = 0; i < TESTDATA.length; i++) {
    149             map.put((String)TESTDATA[i][0], TESTDATA[i][1]);
    150         }
    151 
    152         logln("Test for get(String)");
    153         for (int i = 0; i < TESTCASES.length; i++) {
    154             itr = map.get((String)TESTCASES[i][0]);
    155             checkResult("get(String) case " + i, itr, TESTCASES[i][1]);
    156         }
    157 
    158         logln("Test for get(String, int)");
    159         StringBuffer textBuf = new StringBuffer();
    160         for (int i = 0; i < TESTCASES.length; i++) {
    161             textBuf.setLength(0);
    162             for (int j = 0; j < i; j++) {
    163                 textBuf.append('X');
    164             }
    165             textBuf.append(TESTCASES[i][0]);
    166             itr = map.get(textBuf.toString(), i);
    167             checkResult("get(String, int) case " + i, itr, TESTCASES[i][1]);
    168         }
    169 
    170         logln("Test for ParseState");
    171         for (int i = 0; i < TESTCASES_PARSE.length; i++) {
    172             String test = (String) TESTCASES_PARSE[i][0];
    173             Object[] expecteds = (Object[]) TESTCASES_PARSE[i][1];
    174             checkParse(map, test, expecteds, true);
    175         }
    176 
    177         // Add duplicated entry
    178         map.put("Sunday", FOO);
    179         // Add duplicated entry with different casing
    180         map.put("sunday", BAR);
    181 
    182         // Make sure the all entries are returned
    183         itr = map.get("Sunday");
    184         checkResult("Get Sunday", itr, new Object[]{FOO, SUN});
    185     }
    186 
    187     @Test
    188     public void TestCaseInsensitive() {
    189         Iterator itr = null;
    190         TextTrieMap map = new TextTrieMap(true);
    191         for (int i = 0; i < TESTDATA.length; i++) {
    192             map.put((String)TESTDATA[i][0], TESTDATA[i][1]);
    193         }
    194 
    195         logln("Test for get(String)");
    196         for (int i = 0; i < TESTCASES.length; i++) {
    197             itr = map.get((String)TESTCASES[i][0]);
    198             checkResult("get(String) case " + i, itr, TESTCASES[i][2]);
    199         }
    200 
    201         logln("Test for get(String, int)");
    202         StringBuffer textBuf = new StringBuffer();
    203         for (int i = 0; i < TESTCASES.length; i++) {
    204             textBuf.setLength(0);
    205             for (int j = 0; j < i; j++) {
    206                 textBuf.append('X');
    207             }
    208             textBuf.append(TESTCASES[i][0]);
    209             itr = map.get(textBuf.toString(), i);
    210             checkResult("get(String, int) case " + i, itr, TESTCASES[i][2]);
    211         }
    212 
    213         logln("Test for ParseState");
    214         for (int i = 0; i < TESTCASES_PARSE.length; i++) {
    215             String test = (String) TESTCASES_PARSE[i][0];
    216             Object[] expecteds = (Object[]) TESTCASES_PARSE[i][1];
    217             checkParse(map, test, expecteds, false);
    218         }
    219 
    220         // Add duplicated entry
    221         map.put("Sunday", FOO);
    222         // Add duplicated entry with different casing
    223         map.put("sunday", BAR);
    224 
    225         // Make sure the all entries are returned
    226         itr = map.get("Sunday");
    227         checkResult("Get Sunday", itr, new Object[]{SUN, FOO, BAR});
    228     }
    229 
    230     private void checkParse(TextTrieMap map, String text, Object[] rawExpecteds, boolean caseSensitive) {
    231         // rawExpecteds has even-valued indices for case sensitive and odd-valued indicies for case insensitive
    232         // Get out only the values that we want.
    233         Object[] expecteds = null;
    234         for (int i=rawExpecteds.length/2-1; i>=0; i--) {
    235             int j = i*2+(caseSensitive?0:1);
    236             if (rawExpecteds[j] != null) {
    237                 if (expecteds == null) {
    238                     expecteds = new Object[i+1];
    239                 }
    240                 expecteds[i] = rawExpecteds[j];
    241             }
    242         }
    243         if (expecteds == null) {
    244             expecteds = new Object[0];
    245         }
    246 
    247         TextTrieMap.ParseState state = null;
    248         for (int charOffset=0, cpOffset=0; charOffset < text.length(); cpOffset++) {
    249             int cp = Character.codePointAt(text, charOffset);
    250             if (state == null) {
    251                 state = map.openParseState(cp);
    252             }
    253             if (state == null) {
    254                 assertEquals("Expected matches, but no matches are available", 0, expecteds.length);
    255                 break;
    256             }
    257             state.accept(cp);
    258             if (cpOffset < expecteds.length - 1) {
    259                 assertFalse(
    260                         "In middle of parse sequence, but atEnd() is true: '" + text + "' offset " + charOffset,
    261                         state.atEnd());
    262             } else if (cpOffset == expecteds.length) {
    263                 // Note: it possible for atEnd() to be either true or false at expecteds.length - 1;
    264                 // if true, we are at the end of the input string; if false, there is still input string
    265                 // left to be consumed, but we don't know if there are remaining matches.
    266                 assertTrue(
    267                         "At end of parse sequence, but atEnd() is false: '" + text + "' offset " + charOffset,
    268                         state.atEnd());
    269                 break;
    270             }
    271             Object expected = expecteds[cpOffset];
    272             Iterator actual = state.getCurrentMatches();
    273             checkResult("ParseState '" + text + "' offset " + charOffset, actual, expected);
    274             charOffset += Character.charCount(cp);
    275         }
    276     }
    277 
    278     private boolean eql(Object o1, Object o2) {
    279         if (o1 == null || o2 == null) {
    280             if (o1 == null && o2 == null) {
    281                 return true;
    282             }
    283             return false;
    284         }
    285         return o1.equals(o2);
    286     }
    287 
    288     private void checkResult(String memo, Iterator itr, Object expected) {
    289         if (itr == null) {
    290             if (expected != null) {
    291                 String expectedStr = (expected instanceof Object[])
    292                         ? Arrays.toString((Object[]) expected)
    293                         : expected.toString();
    294                 errln("FAIL: Empty results: " + memo + ": Expected: " + expectedStr);
    295             }
    296             return;
    297         }
    298         if (expected == null && itr != null) {
    299             errln("FAIL: Empty result is expected");
    300             return;
    301         }
    302 
    303         Object[] exp;
    304         if (expected instanceof Object[]) {
    305             exp = (Object[])expected;
    306         } else {
    307             exp = new Object[]{expected};
    308         }
    309 
    310         boolean[] found = new boolean[exp.length];
    311         while (itr.hasNext()) {
    312             Object val = itr.next();
    313             for (int i = 0; i < exp.length; i++) {
    314                 if (eql(exp[i], val)) {
    315                     found[i] = true;
    316                 }
    317             }
    318         }
    319         for (int i = 0; i < exp.length; i++) {
    320             if (found[i] == false) {
    321                 errln("FAIL: The search result does not contain " + exp[i]);
    322             }
    323         }
    324     }
    325 }
    326