Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 1 | // © 2016 and later: Unicode, Inc. and others. |
| 2 | // License & terms of use: http://www.unicode.org/copyright.html |
| 3 | /******************************************************************** |
| 4 | * COPYRIGHT: |
| 5 | * Copyright (c) 1997-2013 International Business Machines Corporation and |
| 6 | * others. All Rights Reserved. |
| 7 | ********************************************************************/ |
| 8 | |
| 9 | /*********************************************************************** |
| 10 | * Modification history |
| 11 | * Date Name Description |
| 12 | * 07/09/2007 srl Copied from dadrcoll.cpp |
| 13 | ***********************************************************************/ |
| 14 | |
| 15 | #include "unicode/utypes.h" |
| 16 | |
| 17 | #if !UCONFIG_NO_FORMATTING |
| 18 | |
| 19 | #include "unicode/tstdtmod.h" |
| 20 | #include "tsdate.h" |
| 21 | #include "dadrfmt.h" |
| 22 | #include "unicode/calendar.h" |
| 23 | #include "intltest.h" |
| 24 | #include <string.h> |
| 25 | #include "unicode/schriter.h" |
| 26 | #include "unicode/regex.h" |
| 27 | #include "unicode/smpdtfmt.h" |
| 28 | #include "dbgutil.h" |
| 29 | #include "fldset.h" |
| 30 | |
| 31 | |
| 32 | #include <stdio.h> |
| 33 | |
| 34 | DataDrivenFormatTest::DataDrivenFormatTest() { |
| 35 | UErrorCode status = U_ZERO_ERROR; |
| 36 | driver = TestDataModule::getTestDataModule("format", *this, status); |
| 37 | } |
| 38 | |
| 39 | DataDrivenFormatTest::~DataDrivenFormatTest() { |
| 40 | delete driver; |
| 41 | } |
| 42 | |
| 43 | void DataDrivenFormatTest::runIndexedTest(int32_t index, UBool exec, |
| 44 | const char* &name, char* /*par */) { |
| 45 | if (driver != NULL) { |
| 46 | if (exec) { |
| 47 | // logln("Begin "); |
| 48 | } |
| 49 | const DataMap *info= NULL; |
| 50 | UErrorCode status= U_ZERO_ERROR; |
| 51 | TestData *testData = driver->createTestData(index, status); |
| 52 | if (U_SUCCESS(status)) { |
| 53 | name = testData->getName(); |
| 54 | if (testData->getInfo(info, status)) { |
| 55 | log(info->getString("Description", status)); |
| 56 | } |
| 57 | if (exec) { |
| 58 | log(name); |
| 59 | logln("---"); |
| 60 | logln(""); |
| 61 | |
| 62 | processTest(testData); |
| 63 | } |
| 64 | delete testData; |
| 65 | } else { |
| 66 | name = ""; |
| 67 | } |
| 68 | } else { |
| 69 | dataerrln("format/DataDriven*Test data (format.res) not initialized!"); |
| 70 | name = ""; |
| 71 | } |
| 72 | |
| 73 | } |
| 74 | |
| 75 | |
| 76 | |
| 77 | /* |
| 78 | * Headers { "locale", "zone", "spec", "date", "str"} |
| 79 | // locale: locale including calendar type |
| 80 | // zone: time zone name, or "" to not explicitly set zone |
| 81 | // spec: either 'PATTERN=y mm h' etc, or 'DATE=SHORT,TIME=LONG' |
| 82 | // date: either an unsigned long (millis), or a calendar spec ERA=0,YEAR=1, etc.. applied to the calendar type specified by the locale |
| 83 | // str: the expected unicode string |
| 84 | Cases { |
| 85 | { |
| 86 | "en_US@calendar=gregorian", |
| 87 | "", |
| 88 | "DATE=SHORT,TIME=SHORT", |
| 89 | "ERA=1,YEAR=2007,MONTH=AUGUST,DATE=8,HOUR=18,MINUTE=54,SECOND=12", |
| 90 | "8/8/2007 6:54pm" |
| 91 | }, |
| 92 | * */ |
| 93 | |
| 94 | |
| 95 | void DataDrivenFormatTest::testConvertDate(TestData *testData, |
| 96 | const DataMap * /* settings */, UBool fmt) { |
| 97 | UnicodeString kPATTERN("PATTERN="); // TODO: static |
| 98 | UnicodeString kMILLIS("MILLIS="); // TODO: static |
| 99 | UnicodeString kRELATIVE_MILLIS("RELATIVE_MILLIS="); // TODO: static |
| 100 | UnicodeString kRELATIVE_ADD("RELATIVE_ADD:"); // TODO: static |
| 101 | |
| 102 | UErrorCode status = U_ZERO_ERROR; |
| 103 | SimpleDateFormat basicFmt(UnicodeString("EEE MMM dd yyyy / YYYY'-W'ww-ee"), |
| 104 | status); |
| 105 | if (U_FAILURE(status)) { |
| 106 | dataerrln("FAIL: Couldn't create basic SimpleDateFormat: %s", |
| 107 | u_errorName(status)); |
| 108 | return; |
| 109 | } |
| 110 | |
| 111 | const DataMap *currentCase= NULL; |
| 112 | // Start the processing |
| 113 | int n = 0; |
| 114 | while (testData->nextCase(currentCase, status)) { |
| 115 | char calLoc[256] = ""; |
| 116 | DateTimeStyleSet styleSet; |
| 117 | UnicodeString pattern; |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 118 | UBool usePattern = false; |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 119 | (void)usePattern; // Suppress unused warning. |
| 120 | CalendarFieldsSet fromSet; |
| 121 | UDate fromDate = 0; |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 122 | UBool useDate = false; |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 123 | |
| 124 | UDate now = Calendar::getNow(); |
| 125 | |
| 126 | ++n; |
| 127 | |
| 128 | char theCase[200]; |
| 129 | sprintf(theCase, "case %d:", n); |
| 130 | UnicodeString caseString(theCase, ""); |
| 131 | |
| 132 | // load params |
| 133 | UnicodeString locale = currentCase->getString("locale", status); |
| 134 | if (U_FAILURE(status)) { |
| 135 | errln("case %d: No 'locale' line.", n); |
| 136 | continue; |
| 137 | } |
| 138 | UnicodeString zone = currentCase->getString("zone", status); |
| 139 | if (U_FAILURE(status)) { |
| 140 | errln("case %d: No 'zone' line.", n); |
| 141 | continue; |
| 142 | } |
| 143 | UnicodeString spec = currentCase->getString("spec", status); |
| 144 | if(U_FAILURE(status)) { |
| 145 | errln("case %d: No 'spec' line.", n); |
| 146 | continue; |
| 147 | } |
| 148 | UnicodeString date = currentCase->getString("date", status); |
| 149 | if(U_FAILURE(status)) { |
| 150 | errln("case %d: No 'date' line.", n); |
| 151 | continue; |
| 152 | } |
| 153 | UnicodeString expectStr= currentCase->getString("str", status); |
| 154 | if(U_FAILURE(status)) { |
| 155 | errln("case %d: No 'str' line.", n); |
| 156 | continue; |
| 157 | } |
| 158 | |
| 159 | DateFormat *format = NULL; |
| 160 | |
| 161 | // Process: 'locale' |
| 162 | locale.extract(0, locale.length(), calLoc, (const char*)0); // default codepage. Invariant codepage doesn't have '@'! |
| 163 | Locale loc(calLoc); |
| 164 | if(spec.startsWith(kPATTERN)) { |
| 165 | pattern = UnicodeString(spec,kPATTERN.length()); |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 166 | usePattern = true; |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 167 | format = new SimpleDateFormat(pattern, loc, status); |
| 168 | if(U_FAILURE(status)) { |
| 169 | errln("case %d: could not create SimpleDateFormat from pattern: %s", n, u_errorName(status)); |
| 170 | continue; |
| 171 | } |
| 172 | } else { |
| 173 | if(styleSet.parseFrom(spec, status)<0 || U_FAILURE(status)) { |
| 174 | errln("case %d: could not parse spec as style fields: %s", n, u_errorName(status)); |
| 175 | continue; |
| 176 | } |
| 177 | format = DateFormat::createDateTimeInstance((DateFormat::EStyle)styleSet.getDateStyle(), (DateFormat::EStyle)styleSet.getTimeStyle(), loc); |
| 178 | if(format == NULL ) { |
| 179 | errln("case %d: could not create SimpleDateFormat from styles.", n); |
| 180 | continue; |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | Calendar *cal = Calendar::createInstance(loc, status); |
| 185 | if(U_FAILURE(status)) { |
| 186 | errln("case %d: could not create calendar from %s", n, calLoc); |
| 187 | } |
| 188 | |
| 189 | if (zone.length() > 0) { |
| 190 | TimeZone * tz = TimeZone::createTimeZone(zone); |
| 191 | cal->setTimeZone(*tz); |
| 192 | format->setTimeZone(*tz); |
| 193 | delete tz; |
| 194 | } |
| 195 | |
| 196 | // parse 'date' |
| 197 | if(date.startsWith(kMILLIS)) { |
| 198 | UnicodeString millis = UnicodeString(date, kMILLIS.length()); |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 199 | useDate = true; |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 200 | fromDate = udbg_stod(millis); |
| 201 | } else if(date.startsWith(kRELATIVE_MILLIS)) { |
| 202 | UnicodeString millis = UnicodeString(date, kRELATIVE_MILLIS.length()); |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 203 | useDate = true; |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 204 | fromDate = udbg_stod(millis) + now; |
| 205 | } else if(date.startsWith(kRELATIVE_ADD)) { |
| 206 | UnicodeString add = UnicodeString(date, kRELATIVE_ADD.length()); // "add" is a string indicating which fields to add |
| 207 | if(fromSet.parseFrom(add, status)<0 || U_FAILURE(status)) { |
| 208 | errln("case %d: could not parse date as RELATIVE_ADD calendar fields: %s", n, u_errorName(status)); |
| 209 | continue; |
| 210 | } |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 211 | useDate=true; |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 212 | cal->clear(); |
| 213 | cal->setTime(now, status); |
| 214 | for (int q=0; q<UCAL_FIELD_COUNT; q++) { |
| 215 | if (fromSet.isSet((UCalendarDateFields)q)) { |
| 216 | //int32_t oldv = cal->get((UCalendarDateFields)q, status); |
| 217 | if (q == UCAL_DATE) { |
| 218 | cal->add((UCalendarDateFields)q, |
| 219 | fromSet.get((UCalendarDateFields)q), status); |
| 220 | } else { |
| 221 | cal->set((UCalendarDateFields)q, |
| 222 | fromSet.get((UCalendarDateFields)q)); |
| 223 | } |
| 224 | //int32_t newv = cal->get((UCalendarDateFields)q, status); |
| 225 | } |
| 226 | } |
| 227 | fromDate = cal->getTime(status); |
| 228 | if(U_FAILURE(status)) { |
| 229 | errln("case %d: could not apply date as RELATIVE_ADD calendar fields: %s", n, u_errorName(status)); |
| 230 | continue; |
| 231 | } |
| 232 | } else if(fromSet.parseFrom(date, status)<0 || U_FAILURE(status)) { |
| 233 | errln("case %d: could not parse date as calendar fields: %s", n, u_errorName(status)); |
| 234 | continue; |
| 235 | } |
| 236 | |
| 237 | // now, do it. |
| 238 | if (fmt) { |
| 239 | FieldPosition pos; |
| 240 | // logln((UnicodeString)"#"+n+" "+locale+"/"+from+" >>> "+toCalLoc+"/" |
| 241 | // +to); |
| 242 | cal->clear(); |
| 243 | UnicodeString output; |
| 244 | output.remove(); |
| 245 | |
| 246 | if(useDate) { |
| 247 | // cal->setTime(fromDate, status); |
| 248 | // if(U_FAILURE(status)) { |
| 249 | // errln("case %d: could not set time on calendar: %s", n, u_errorName(status)); |
| 250 | // continue; |
| 251 | // } |
| 252 | format->format(fromDate, output, pos, status); |
| 253 | } else { |
| 254 | fromSet.setOnCalendar(cal, status); |
| 255 | if(U_FAILURE(status)) { |
| 256 | errln("case %d: could not set fields on calendar: %s", n, u_errorName(status)); |
| 257 | continue; |
| 258 | } |
| 259 | format->format(*cal, output, pos); |
| 260 | } |
| 261 | |
| 262 | // check erro result from 'format' |
| 263 | if(U_FAILURE(status)) { |
| 264 | errln("case %d: could not format(): %s", n, u_errorName(status)); // TODO: use 'pos' |
| 265 | } |
| 266 | // if(pos.getBeginIndex()==0 && pos.getEndIndex()==0) { // TODO: more precise error? |
| 267 | // errln("WARNING: case %d: format's pos returned (0,0) - error ??", n); |
| 268 | // } |
| 269 | |
| 270 | if(output == expectStr) { |
| 271 | logln(caseString+": format: SUCCESS! "+UnicodeString("expect=output=")+output); |
| 272 | } else { |
| 273 | UnicodeString result; |
| 274 | UnicodeString result2; |
| 275 | errln(caseString+": format: output!=expectStr, got " + *udbg_escape(output, &result) + " expected " + *udbg_escape(expectStr, &result2)); |
| 276 | } |
| 277 | } else { |
| 278 | cal->clear(); |
| 279 | ParsePosition pos; |
| 280 | format->parse(expectStr,*cal,pos); |
| 281 | if(useDate) { |
| 282 | UDate gotDate = cal->getTime(status); |
| 283 | if(U_FAILURE(status)) { |
| 284 | errln(caseString+": parse: could not get time on calendar: "+UnicodeString(u_errorName(status))); |
| 285 | continue; |
| 286 | } |
| 287 | if(gotDate == fromDate) { |
| 288 | logln(caseString+": parse: SUCCESS! "+UnicodeString("gotDate=parseDate=")+expectStr); |
| 289 | } else { |
| 290 | UnicodeString expectDateStr, gotDateStr; |
| 291 | basicFmt.format(fromDate,expectDateStr); |
| 292 | basicFmt.format(gotDate,gotDateStr); |
| 293 | errln(caseString+": parse: FAIL. parsed '"+expectStr+"' and got "+gotDateStr+", expected " + expectDateStr); |
| 294 | } |
| 295 | } else { |
| 296 | // Calendar *cal2 = cal->clone(); |
| 297 | // cal2->clear(); |
| 298 | // fromSet.setOnCalendar(cal2, status); |
| 299 | if(U_FAILURE(status)) { |
| 300 | errln("case %d: parse: could not set fields on calendar: %s", n, u_errorName(status)); |
| 301 | continue; |
| 302 | } |
| 303 | |
| 304 | CalendarFieldsSet diffSet; |
| 305 | // diffSet.clear(); |
| 306 | if (!fromSet.matches(cal, diffSet, status)) { |
| 307 | UnicodeString diffs = diffSet.diffFrom(fromSet, status); |
| 308 | errln((UnicodeString)"FAIL: "+caseString |
| 309 | +", Differences: '"+ diffs |
| 310 | +"', status: "+ u_errorName(status)); |
| 311 | } else if (U_FAILURE(status)) { |
| 312 | errln("FAIL: "+caseString+" parse SET SOURCE calendar Failed to match: " |
| 313 | +u_errorName(status)); |
| 314 | } else { |
| 315 | logln("PASS: "+caseString+" parse."); |
| 316 | } |
| 317 | |
| 318 | |
| 319 | |
| 320 | } |
| 321 | } |
| 322 | delete cal; |
| 323 | delete format; |
| 324 | |
| 325 | } |
| 326 | // delete basicFmt; |
| 327 | } |
| 328 | |
| 329 | void DataDrivenFormatTest::processTest(TestData *testData) { |
| 330 | //Format *cal= NULL; |
| 331 | //const UChar *arguments= NULL; |
| 332 | //int32_t argLen = 0; |
| 333 | char testType[256] = ""; |
| 334 | const DataMap *settings= NULL; |
| 335 | //const UChar *type= NULL; |
| 336 | UErrorCode status = U_ZERO_ERROR; |
| 337 | UnicodeString testSetting; |
| 338 | int n = 0; |
| 339 | while (testData->nextSettings(settings, status)) { |
| 340 | status = U_ZERO_ERROR; |
| 341 | // try to get a locale |
| 342 | testSetting = settings->getString("Type", status); |
| 343 | if (U_SUCCESS(status)) { |
| 344 | if ((++n)>0) { |
| 345 | logln("---"); |
| 346 | } |
| 347 | logln(testSetting + "---"); |
| 348 | testSetting.extract(0, testSetting.length(), testType, ""); |
| 349 | } else { |
| 350 | errln("Unable to extract 'Type'. Skipping.."); |
| 351 | continue; |
| 352 | } |
| 353 | |
| 354 | if (!strcmp(testType, "date_format")) { |
| 355 | testConvertDate(testData, settings, true); |
| 356 | } else if (!strcmp(testType, "date_parse")) { |
| 357 | testConvertDate(testData, settings, false); |
| 358 | } else { |
| 359 | errln("Unknown type: %s", testType); |
| 360 | } |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | #endif |