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