1// Copyright (C) 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
34DataDrivenFormatTest::DataDrivenFormatTest() {
35    UErrorCode status = U_ZERO_ERROR;
36    driver = TestDataModule::getTestDataModule("format", *this, status);
37}
38
39DataDrivenFormatTest::~DataDrivenFormatTest() {
40    delete driver;
41}
42
43void 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
95void 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;
118        UBool usePattern = FALSE;
119        (void)usePattern;   // Suppress unused warning.
120        CalendarFieldsSet fromSet;
121        UDate fromDate = 0;
122        UBool useDate = FALSE;
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());
166            usePattern = TRUE;
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());
199            useDate = TRUE;
200            fromDate = udbg_stod(millis);
201        } else if(date.startsWith(kRELATIVE_MILLIS)) {
202            UnicodeString millis = UnicodeString(date, kRELATIVE_MILLIS.length());
203            useDate = TRUE;
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            }
211            useDate=TRUE;
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
329void 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
365