1// Copyright (C) 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/***********************************************************************
4 * Copyright (c) 1997-2009, International Business Machines Corporation
5 * and others. All Rights Reserved.
6 ***********************************************************************/
7
8#include "unicode/utypes.h"
9
10#if !UCONFIG_NO_FORMATTING
11
12#include "unicode/datefmt.h"
13#include "unicode/smpdtfmt.h"
14#include "tsdate.h"
15#include "putilimp.h"
16
17#include <float.h>
18#include <stdlib.h>
19#include <math.h>
20
21const double IntlTestDateFormat::ONEYEAR = 365.25 * ONEDAY; // Approximate
22
23IntlTestDateFormat::~IntlTestDateFormat() {}
24
25/**
26 * This test does round-trip testing (format -> parse -> format -> parse -> etc.) of
27 * DateFormat.
28 */
29// par is ignored throughout this file
30void IntlTestDateFormat::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
31{
32    if (exec) logln("TestSuite DateFormat");
33    switch (index) {
34        case 0: name = "GenericTest";
35            if (exec) {
36                logln(name);
37                fFormat = DateFormat::createInstance();
38                fTestName = "createInstance";
39                fLimit = 3;
40                testFormat(/* par */);
41            }
42            break;
43        case 1: name = "DefaultLocale";
44            if (exec) {
45                logln(name);
46                testLocale(/*par, */Locale::getDefault(), "Default Locale");
47            }
48            break;
49
50        case 2: name = "TestAvailableLocales";
51            if (exec) {
52                logln(name);
53                testAvailableLocales(/* par */);
54            }
55            break;
56
57        case 3: name = "MonsterTest";
58            if (exec) {
59                logln(name);
60                monsterTest(/*par*/);
61            }
62            break;
63
64        default: name = ""; break;
65    }
66}
67
68void
69IntlTestDateFormat::testLocale(/*char* par, */const Locale& locale, const UnicodeString& localeName)
70{
71    DateFormat::EStyle timeStyle, dateStyle;
72
73    // For patterns including only time information and a timezone, it may take
74    // up to three iterations, since the timezone may shift as the year number
75    // is determined.  For other patterns, 2 iterations should suffice.
76    fLimit = 3;
77
78    for(timeStyle = (DateFormat::EStyle)0;
79        timeStyle < (DateFormat::EStyle)4;
80        timeStyle = (DateFormat::EStyle) (timeStyle+1))
81    {
82        fTestName = (UnicodeString) "Time test " + (int32_t) timeStyle + " (" + localeName + ")";
83        fFormat = DateFormat::createTimeInstance(timeStyle, locale);
84        testFormat(/* par */);
85    }
86
87    fLimit = 2;
88
89    for(dateStyle = (DateFormat::EStyle)0;
90        dateStyle < (DateFormat::EStyle)4;
91        dateStyle = (DateFormat::EStyle) (dateStyle+1))
92    {
93        fTestName = (UnicodeString) "Date test " + (int32_t) dateStyle + " (" + localeName + ")";
94        fFormat = DateFormat::createDateInstance(dateStyle, locale);
95        testFormat(/* par */);
96    }
97
98    for(dateStyle = (DateFormat::EStyle)0;
99        dateStyle < (DateFormat::EStyle)4;
100        dateStyle = (DateFormat::EStyle) (dateStyle+1))
101    {
102        for(timeStyle = (DateFormat::EStyle)0;
103            timeStyle < (DateFormat::EStyle)4;
104            timeStyle = (DateFormat::EStyle) (timeStyle+1))
105        {
106            fTestName = (UnicodeString) "DateTime test " + (int32_t) dateStyle + "/" + (int32_t) timeStyle + " (" + localeName + ")";
107            fFormat = DateFormat::createDateTimeInstance(dateStyle, timeStyle, locale);
108            testFormat(/* par */);
109        }
110    }
111}
112
113void IntlTestDateFormat::testFormat(/* char* par */)
114{
115    if (fFormat == 0)
116    {
117        dataerrln("FAIL: DateFormat creation failed");
118        return;
119    }
120
121    describeTest();
122
123    UDate now = Calendar::getNow();
124    tryDate(0);
125    tryDate(1278161801778.0);
126    tryDate(5264498352317.0);   // Sunday, October 28, 2136 8:39:12 AM PST
127    tryDate(9516987689250.0);   // In the year 2271
128    tryDate(now);
129    // Shift 6 months into the future, AT THE SAME TIME OF DAY.
130    // This will test the DST handling.
131    tryDate(now + 6.0*30*ONEDAY);
132
133    UDate limit = now * 10; // Arbitrary limit
134    for (int32_t i=0; i<3; ++i)
135        tryDate(uprv_floor(randDouble() * limit));
136
137    delete fFormat;
138}
139
140void
141IntlTestDateFormat::describeTest()
142{
143    // Assume it's a SimpleDateFormat and get some info
144    SimpleDateFormat *s = (SimpleDateFormat*)fFormat;
145    UnicodeString str;
146    logln(fTestName + " Pattern " + s->toPattern(str));
147}
148
149void IntlTestDateFormat::tryDate(UDate theDate)
150{
151    const int32_t DEPTH = 10;
152    UDate date[DEPTH];
153    UnicodeString string[DEPTH];
154
155    int32_t dateMatch = 0;
156    int32_t stringMatch = 0;
157    UBool dump = FALSE;
158#if defined (U_CAL_DEBUG)
159    dump = TRUE;
160#endif
161    int32_t i;
162
163    date[0] = theDate;
164    fFormat->format(theDate, string[0]);
165
166    for (i=1; i<DEPTH; ++i)
167    {
168        UErrorCode status = U_ZERO_ERROR;
169        date[i] = fFormat->parse(string[i-1], status);
170        if (U_FAILURE(status))
171        {
172            describeTest();
173            errln("**** FAIL: Parse of " + prettify(string[i-1], FALSE) + " failed.");
174            dump = TRUE;
175            break;
176        }
177        fFormat->format(date[i], string[i]);
178        if (dateMatch == 0 && date[i] == date[i-1])
179            dateMatch = i;
180        else if (dateMatch > 0 && date[i] != date[i-1])
181        {
182            describeTest();
183            errln("**** FAIL: Date mismatch after match for " + string[i]);
184            dump = TRUE;
185            break;
186        }
187        if (stringMatch == 0 && string[i] == string[i-1])
188            stringMatch = i;
189        else if (stringMatch > 0 && string[i] != string[i-1])
190        {
191            describeTest();
192            errln("**** FAIL: String mismatch after match for " + string[i]);
193            dump = TRUE;
194            break;
195        }
196        if (dateMatch > 0 && stringMatch > 0)
197            break;
198    }
199    if (i == DEPTH)
200        --i;
201
202    if (stringMatch > fLimit || dateMatch > fLimit)
203    {
204        describeTest();
205        errln((UnicodeString)"**** FAIL: No string and/or date match within " + fLimit
206            + " iterations for the Date " + string[0] + "\t(" + theDate + ").");
207        dump = TRUE;
208    }
209
210    if (dump)
211    {
212        for (int32_t k=0; k<=i; ++k)
213        {
214            logln((UnicodeString)"" + k + ": " + date[k] + " F> " +
215                  string[k] + " P> ");
216        }
217    }
218}
219
220// Return a random double from 0.01 to 1, inclusive
221double IntlTestDateFormat::randDouble()
222{
223    // Assume 8-bit (or larger) rand values.  Also assume
224    // that the system rand() function is very poor, which it always is.
225    double d=0.0;
226    uint32_t i;
227    char* poke = (char*)&d;
228    do {
229        do {
230            for (i=0; i < sizeof(double); ++i)
231            {
232                poke[i] = (char)(rand() & 0xFF);
233            }
234        } while (uprv_isNaN(d) || uprv_isInfinite(d));
235
236        if (d < 0.0)
237            d = -d;
238        if (d > 0.0)
239        {
240            double e = uprv_floor(log10(d));
241            if (e < -2.0)
242                d *= uprv_pow10((int32_t)(-e-2));
243            else if (e > -1.0)
244                d /= uprv_pow10((int32_t)(e+1));
245        }
246    // While this is not a real normalized number make another one.
247    } while (uprv_isNaN(d) || uprv_isInfinite(d)
248        || !((-DBL_MAX < d && d < DBL_MAX) || (d < -DBL_MIN && DBL_MIN < d)));
249    return d;
250}
251
252void IntlTestDateFormat::testAvailableLocales(/* char* par */)
253{
254    int32_t count = 0;
255    const Locale* locales = DateFormat::getAvailableLocales(count);
256    logln((UnicodeString)"" + count + " available locales");
257    if (locales && count)
258    {
259        UnicodeString name;
260        UnicodeString all;
261        for (int32_t i=0; i<count; ++i)
262        {
263            if (i!=0) all += ", ";
264            all += locales[i].getName();
265        }
266        logln(all);
267    }
268    else dataerrln((UnicodeString)"**** FAIL: Zero available locales or null array pointer");
269}
270
271void IntlTestDateFormat::monsterTest(/*char *par*/)
272{
273    int32_t count;
274    const Locale* locales = DateFormat::getAvailableLocales(count);
275    if (locales && count)
276    {
277        if (quick && count > 3) {
278            logln("quick test: testing just 3 locales!");
279            count = 3;
280        }
281        for (int32_t i=0; i<count; ++i)
282        {
283            UnicodeString name = UnicodeString(locales[i].getName(), "");
284            logln((UnicodeString)"Testing " + name + "...");
285            testLocale(/*par, */locales[i], name);
286        }
287    }
288}
289
290#endif /* #if !UCONFIG_NO_FORMATTING */
291