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