Home | History | Annotate | Download | only in format
      1 /* GENERATED SOURCE. DO NOT MODIFY. */
      2 //  2016 and later: Unicode, Inc. and others.
      3 // License & terms of use: http://www.unicode.org/copyright.html#License
      4 /*
      5  *******************************************************************************
      6  * Copyright (C) 2001-2010, International Business Machines Corporation and    *
      7  * others. All Rights Reserved.                                                *
      8  *******************************************************************************
      9  */
     10 
     11 /**
     12  * Port From:   ICU4C v1.8.1 : format : DateFormatRoundTripTest
     13  * Source File: $ICU4CRoot/source/test/intltest/dtfmtrtts.cpp
     14  **/
     15 
     16 package android.icu.dev.test.format;
     17 
     18 import java.text.FieldPosition;
     19 import java.text.ParseException;
     20 import java.util.Date;
     21 import java.util.Locale;
     22 import java.util.Random;
     23 
     24 import org.junit.Test;
     25 import org.junit.runner.RunWith;
     26 import org.junit.runners.JUnit4;
     27 
     28 import android.icu.dev.test.TestFmwk;
     29 import android.icu.text.DateFormat;
     30 import android.icu.text.SimpleDateFormat;
     31 import android.icu.util.Calendar;
     32 import android.icu.util.GregorianCalendar;
     33 import android.icu.util.TimeZone;
     34 import android.icu.testsharding.MainTestShard;
     35 
     36 /**
     37  * Performs round-trip tests for DateFormat
     38  **/
     39 @MainTestShard
     40 @RunWith(JUnit4.class)
     41 public class DateFormatRoundTripTest extends TestFmwk {
     42     public boolean INFINITE = false;
     43     public boolean quick = true;
     44     private SimpleDateFormat dateFormat;
     45     private Calendar getFieldCal;
     46     private int SPARSENESS = 18;
     47     private int TRIALS = 4;
     48     private int DEPTH = 5;
     49     private Random ran;
     50 
     51     // TODO: test is randomly failing depending on the randomly generated date
     52     @Test
     53     public void TestDateFormatRoundTrip() {
     54         dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss.SSS zzz yyyy G");
     55         getFieldCal = Calendar.getInstance();
     56         ran = createRandom(); // use test framework's random seed
     57 
     58         final Locale[] avail = DateFormat.getAvailableLocales();
     59         int locCount = avail.length;
     60         logln("DateFormat available locales: " + locCount);
     61         if (quick) {
     62             if (locCount > 5)
     63                 locCount = 5;
     64             logln("Quick mode: only testing first 5 Locales");
     65         }
     66         TimeZone tz = TimeZone.getDefault();
     67         logln("Default TimeZone:             " + tz.getID());
     68 
     69         if (INFINITE) {
     70             // Special infinite loop test mode for finding hard to reproduce errors
     71             Locale loc = Locale.getDefault();
     72             logln("ENTERING INFINITE TEST LOOP FOR Locale: " + loc.getDisplayName());
     73             for (;;) {
     74                 _test(loc);
     75             }
     76         } else {
     77             _test(Locale.getDefault());
     78             for (int i = 0; i < locCount; ++i) {
     79                 _test(avail[i]);
     80             }
     81         }
     82     }
     83 
     84     private String styleName(int s) {
     85         switch (s) {
     86             case DateFormat.SHORT :
     87                 return "SHORT";
     88             case DateFormat.MEDIUM :
     89                 return "MEDIUM";
     90             case DateFormat.LONG :
     91                 return "LONG";
     92             case DateFormat.FULL :
     93                 return "FULL";
     94             default :
     95                 return "Unknown";
     96         }
     97     }
     98 
     99     private void _test(Locale loc) {
    100         if (!INFINITE) {
    101             logln("Locale: " + loc.getDisplayName());
    102         }
    103         // Total possibilities = 24
    104         //  4 date
    105         //  4 time
    106         //  16 date-time
    107         boolean[] TEST_TABLE = new boolean[24];
    108         int i = 0;
    109         for (i = 0; i < 24; ++i)
    110             TEST_TABLE[i] = true;
    111 
    112         // If we have some sparseness, implement it here.  Sparseness decreases
    113         // test time by eliminating some tests, up to 23.
    114         for (i = 0; i < SPARSENESS; i++) {
    115             int random = (int) (ran.nextDouble() * 24);
    116             if (random >= 0 && random < 24 && TEST_TABLE[i]) {
    117                 TEST_TABLE[random] = false;
    118             }
    119         }
    120 
    121         int itable = 0;
    122         int style = 0;
    123         for (style = DateFormat.FULL; style <= DateFormat.SHORT; ++style) {
    124             if (TEST_TABLE[itable++]) {
    125                 logln("Testing style " + styleName(style));
    126                 DateFormat df = DateFormat.getDateInstance(style, loc);
    127                 _test(df, false);
    128             }
    129         }
    130 
    131         for (style = DateFormat.FULL; style <= DateFormat.SHORT; ++style) {
    132             if (TEST_TABLE[itable++]) {
    133                 logln("Testing style " + styleName(style));
    134                 DateFormat  df = DateFormat.getTimeInstance(style, loc);
    135                 _test(df, true);
    136             }
    137         }
    138 
    139         for (int dstyle = DateFormat.FULL; dstyle <= DateFormat.SHORT; ++dstyle) {
    140             for (int tstyle = DateFormat.FULL; tstyle <= DateFormat.SHORT; ++tstyle) {
    141                 if (TEST_TABLE[itable++]) {
    142                     logln("Testing dstyle " + styleName(dstyle) + ", tstyle " + styleName(tstyle));
    143                     DateFormat df = DateFormat.getDateTimeInstance(dstyle, tstyle, loc);
    144                     _test(df, false);
    145                 }
    146             }
    147         }
    148     }
    149 
    150     private void _test(DateFormat fmt, boolean timeOnly) {
    151 
    152         if (!(fmt instanceof SimpleDateFormat)) {
    153             errln("DateFormat wasn't a SimpleDateFormat");
    154             return;
    155         }
    156 
    157         String pat = ((SimpleDateFormat) fmt).toPattern();
    158         logln(pat);
    159 
    160         // NOTE TO MAINTAINER
    161         // This indexOf check into the pattern needs to be refined to ignore
    162         // quoted characters.  Currently, this isn't a problem with the locale
    163         // patterns we have, but it may be a problem later.
    164 
    165         boolean hasEra = (pat.indexOf("G") != -1);
    166         boolean hasZoneDisplayName = (pat.indexOf("z") != -1) || (pat.indexOf("v") != -1) || (pat.indexOf("V") != -1);
    167         boolean hasTwoDigitYear = pat.indexOf("yy") >= 0 && pat.indexOf("yyy") < 0;
    168 
    169         // Because patterns contain incomplete data representing the Date,
    170         // we must be careful of how we do the roundtrip.  We start with
    171         // a randomly generated Date because they're easier to generate.
    172         // From this we get a string.  The string is our real starting point,
    173         // because this string should parse the same way all the time.  Note
    174         // that it will not necessarily parse back to the original date because
    175         // of incompleteness in patterns.  For example, a time-only pattern won't
    176         // parse back to the same date.
    177 
    178         try {
    179             for (int i = 0; i < TRIALS; ++i) {
    180                 Date[] d = new Date[DEPTH];
    181                 String[] s = new String[DEPTH];
    182 
    183                 d[0] = generateDate();
    184 
    185                 // We go through this loop until we achieve a match or until
    186                 // the maximum loop count is reached.  We record the points at
    187                 // which the date and the string starts to match.  Once matching
    188                 // starts, it should continue.
    189                 int loop;
    190                 int dmatch = 0; // d[dmatch].getTime() == d[dmatch-1].getTime()
    191                 int smatch = 0; // s[smatch].equals(s[smatch-1])
    192                 for (loop = 0; loop < DEPTH; ++loop) {
    193                     if (loop > 0) {
    194                         d[loop] = fmt.parse(s[loop - 1]);
    195                     }
    196 
    197                     s[loop] = fmt.format(d[loop]);
    198 
    199                     if (loop > 0) {
    200                         if (smatch == 0) {
    201                             boolean match = s[loop].equals(s[loop - 1]);
    202                             if (smatch == 0) {
    203                                 if (match)
    204                                     smatch = loop;
    205                             } else
    206                                 if (!match)
    207                                     errln("FAIL: String mismatch after match");
    208                         }
    209 
    210                         if (dmatch == 0) {
    211                             // {sfb} watch out here, this might not work
    212                             boolean match = d[loop].getTime() == d[loop - 1].getTime();
    213                             if (dmatch == 0) {
    214                                 if (match)
    215                                     dmatch = loop;
    216                             } else
    217                                 if (!match)
    218                                     errln("FAIL: Date mismatch after match");
    219                         }
    220 
    221                         if (smatch != 0 && dmatch != 0)
    222                             break;
    223                     }
    224                 }
    225                 // At this point loop == DEPTH if we've failed, otherwise loop is the
    226                 // max(smatch, dmatch), that is, the index at which we have string and
    227                 // date matching.
    228 
    229                 // Date usually matches in 2.  Exceptions handled below.
    230                 int maxDmatch = 2;
    231                 int maxSmatch = 1;
    232                 if (dmatch > maxDmatch || smatch > maxSmatch) {
    233                     //If the Date is BC
    234                     if (!timeOnly && !hasEra && getField(d[0], Calendar.ERA) == GregorianCalendar.BC) {
    235                         maxDmatch = 3;
    236                         maxSmatch = 2;
    237                     }
    238                     if (hasZoneDisplayName &&
    239                             (fmt.getTimeZone().inDaylightTime(d[0])
    240                                     || fmt.getTimeZone().inDaylightTime(d[1])
    241                                     || d[0].getTime() < 0L /* before 1970 */
    242                                     || hasTwoDigitYear && d[1].getTime() < 0L
    243                                        /* before 1970 as the result of 2-digit year parse */)) {
    244                         maxSmatch = 2;
    245                         if (timeOnly) {
    246                             maxDmatch = 3;
    247                         }
    248                     }
    249                 }
    250 
    251                 if (dmatch > maxDmatch || smatch > maxSmatch) {
    252                     SimpleDateFormat sdf = new SimpleDateFormat("EEEE, MMMM d, yyyy HH:mm:ss, z G", Locale.US);
    253                     logln("Date = " + sdf.format(d[0]) + "; ms = " + d[0].getTime());
    254                     logln("Dmatch: " + dmatch + " maxD: " + maxDmatch + " Smatch:" + smatch + " maxS:" + maxSmatch);
    255                     for (int j = 0; j <= loop && j < DEPTH; ++j) {
    256                         StringBuffer temp = new StringBuffer("");
    257                         FieldPosition pos = new FieldPosition(0);
    258                         logln((j > 0 ? " P> " : "    ") + dateFormat.format(d[j], temp, pos)
    259                             + " F> " + s[j] + (j > 0 && d[j].getTime() == d[j - 1].getTime() ? " d==" : "")
    260                             + (j > 0 && s[j].equals(s[j - 1]) ? " s==" : ""));
    261                     }
    262                     errln("Pattern: " + pat + " failed to match" + "; ms = " + d[0].getTime());
    263                 }
    264             }
    265         } catch (ParseException e) {
    266             errln("Exception: " + e.getMessage());
    267             logln(e.toString());
    268         }
    269     }
    270 
    271     private int getField(Date d, int f) {
    272         getFieldCal.setTime(d);
    273         int ret = getFieldCal.get(f);
    274         return ret;
    275     }
    276 
    277     private Date generateDate() {
    278         double a = ran.nextDouble();
    279         // Now 'a' ranges from 0..1; scale it to range from 0 to 8000 years
    280         a *= 8000;
    281         // Range from (4000-1970) BC to (8000-1970) AD
    282         a -= 4000;
    283         // Now scale up to ms
    284         a *= 365.25 * 24 * 60 * 60 * 1000;
    285         return new Date((long)a);
    286     }
    287 }
    288