1/*
2********************************************************************************
3*   Copyright (C) 2005-2009, International Business Machines
4*   Corporation and others.  All Rights Reserved.
5********************************************************************************
6*
7* File WINNMTST.CPP
8*
9********************************************************************************
10*/
11
12#include "unicode/utypes.h"
13
14#ifdef U_WINDOWS
15
16#if !UCONFIG_NO_FORMATTING
17
18#include "unicode/format.h"
19#include "unicode/numfmt.h"
20#include "unicode/locid.h"
21#include "unicode/ustring.h"
22#include "unicode/testlog.h"
23#include "unicode/utmscale.h"
24
25#include "winnmfmt.h"
26#include "winutil.h"
27#include "winnmtst.h"
28
29#include "cmemory.h"
30#include "cstring.h"
31#include "locmap.h"
32#include "wintz.h"
33#include "uassert.h"
34
35#   define WIN32_LEAN_AND_MEAN
36#   define VC_EXTRALEAN
37#   define NOUSER
38#   define NOSERVICE
39#   define NOIME
40#   define NOMCX
41#   include <windows.h>
42#   include <stdio.h>
43#   include <time.h>
44#   include <float.h>
45#   include <locale.h>
46
47#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
48#define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type))
49#define DELETE_ARRAY(array) uprv_free((void *) (array))
50
51#define STACK_BUFFER_SIZE 32
52
53#define LOOP_COUNT 1000
54
55static UBool initialized = FALSE;
56
57/**
58 * Return a random int64_t where U_INT64_MIN <= ran <= U_INT64_MAX.
59 */
60static uint64_t randomInt64(void)
61{
62    int64_t ran = 0;
63    int32_t i;
64
65    if (!initialized) {
66        srand((unsigned)time(NULL));
67        initialized = TRUE;
68    }
69
70    /* Assume rand has at least 12 bits of precision */
71    for (i = 0; i < sizeof(ran); i += 1) {
72        ((char*)&ran)[i] = (char)((rand() & 0x0FF0) >> 4);
73    }
74
75    return ran;
76}
77
78/**
79 * Return a random double where U_DOUBLE_MIN <= ran <= U_DOUBLE_MAX.
80 */
81static double randomDouble(void)
82{
83    double ran = 0;
84
85    if (!initialized) {
86        srand((unsigned)time(NULL));
87        initialized = TRUE;
88    }
89#if 0
90    int32_t i;
91    do {
92        /* Assume rand has at least 12 bits of precision */
93        for (i = 0; i < sizeof(ran); i += 1) {
94            ((char*)&ran)[i] = (char)((rand() & 0x0FF0) >> 4);
95        }
96    } while (_isnan(ran));
97#else
98	int64_t numerator = randomInt64();
99	int64_t denomenator;
100    do {
101        denomenator = randomInt64();
102    }
103    while (denomenator == 0);
104
105	ran = (double)numerator / (double)denomenator;
106#endif
107
108    return ran;
109}
110
111/**
112 * Return a random int32_t where U_INT32_MIN <= ran <= U_INT32_MAX.
113 */
114static uint32_t randomInt32(void)
115{
116    int32_t ran = 0;
117    int32_t i;
118
119    if (!initialized) {
120        srand((unsigned)time(NULL));
121        initialized = TRUE;
122    }
123
124    /* Assume rand has at least 12 bits of precision */
125    for (i = 0; i < sizeof(ran); i += 1) {
126        ((char*)&ran)[i] = (char)((rand() & 0x0FF0) >> 4);
127    }
128
129    return ran;
130}
131
132static UnicodeString &getWindowsFormat(int32_t lcid, UBool currency, UnicodeString &appendTo, const wchar_t *fmt, ...)
133{
134    wchar_t nStackBuffer[STACK_BUFFER_SIZE];
135    wchar_t *nBuffer = nStackBuffer;
136    va_list args;
137    int result;
138
139    nBuffer[0] = 0x0000;
140
141    /* Due to the arguments causing a result to be <= 23 characters (+2 for NULL and minus),
142    we don't need to reallocate the buffer. */
143    va_start(args, fmt);
144    result = _vsnwprintf(nBuffer, STACK_BUFFER_SIZE, fmt, args);
145    va_end(args);
146
147    /* Just to make sure of the above statement, we add this assert */
148    U_ASSERT(result >=0);
149    // The following code is not used because _vscwprintf isn't available on MinGW at the moment.
150    /*if (result < 0) {
151        int newLength;
152
153        va_start(args, fmt);
154        newLength = _vscwprintf(fmt, args);
155        va_end(args);
156
157        nBuffer = NEW_ARRAY(UChar, newLength + 1);
158
159        va_start(args, fmt);
160        result = _vsnwprintf(nBuffer, newLength + 1, fmt, args);
161        va_end(args);
162    }*/
163
164
165    // vswprintf is sensitive to the locale set by setlocale. For some locales
166    // it doesn't use "." as the decimal separator, which is what GetNumberFormatW
167    // and GetCurrencyFormatW both expect to see.
168    //
169    // To fix this, we scan over the string and replace the first non-digits, except
170    // for a leading "-", with a "."
171    //
172    // Note: (nBuffer[0] == L'-') will evaluate to 1 if there is a leading '-' in the
173    // number, and 0 otherwise.
174    for (wchar_t *p = &nBuffer[nBuffer[0] == L'-']; *p != L'\0'; p += 1) {
175        if (*p < L'0' || *p > L'9') {
176            *p = L'.';
177            break;
178        }
179    }
180
181    wchar_t stackBuffer[STACK_BUFFER_SIZE];
182    wchar_t *buffer = stackBuffer;
183
184    buffer[0] = 0x0000;
185
186    if (currency) {
187        result = GetCurrencyFormatW(lcid, 0, nBuffer, NULL, buffer, STACK_BUFFER_SIZE);
188
189        if (result == 0) {
190            DWORD lastError = GetLastError();
191
192            if (lastError == ERROR_INSUFFICIENT_BUFFER) {
193                int newLength = GetCurrencyFormatW(lcid, 0, nBuffer, NULL, NULL, 0);
194
195                buffer = NEW_ARRAY(UChar, newLength);
196                buffer[0] = 0x0000;
197                GetCurrencyFormatW(lcid, 0, nBuffer, NULL, buffer, newLength);
198            }
199        }
200    } else {
201        result = GetNumberFormatW(lcid, 0, nBuffer, NULL, buffer, STACK_BUFFER_SIZE);
202
203        if (result == 0) {
204            DWORD lastError = GetLastError();
205
206            if (lastError == ERROR_INSUFFICIENT_BUFFER) {
207                int newLength = GetNumberFormatW(lcid, 0, nBuffer, NULL, NULL, 0);
208
209                buffer = NEW_ARRAY(UChar, newLength);
210                buffer[0] = 0x0000;
211                GetNumberFormatW(lcid, 0, nBuffer, NULL, buffer, newLength);
212            }
213        }
214    }
215
216    appendTo.append(buffer, (int32_t) wcslen(buffer));
217
218    if (buffer != stackBuffer) {
219        DELETE_ARRAY(buffer);
220    }
221
222    /*if (nBuffer != nStackBuffer) {
223        DELETE_ARRAY(nBuffer);
224    }*/
225
226    return appendTo;
227}
228
229static void testLocale(const char *localeID, int32_t lcid, NumberFormat *wnf, UBool currency, TestLog *log)
230{
231    for (int n = 0; n < LOOP_COUNT; n += 1) {
232        UnicodeString u3Buffer, u6Buffer, udBuffer;
233        UnicodeString w3Buffer, w6Buffer, wdBuffer;
234        double d    = randomDouble();
235        int32_t i32 = randomInt32();
236        int64_t i64 = randomInt64();
237
238        getWindowsFormat(lcid, currency, wdBuffer, L"%.16f", d);
239
240        getWindowsFormat(lcid, currency, w3Buffer, L"%I32d", i32);
241
242        getWindowsFormat(lcid, currency, w6Buffer, L"%I64d", i64);
243
244        wnf->format(d,   udBuffer);
245        if (udBuffer.compare(wdBuffer) != 0) {
246            UnicodeString locale(localeID);
247
248            log->errln("Double format error for locale " + locale +
249                                    ": got " + udBuffer + " expected " + wdBuffer);
250        }
251
252        wnf->format(i32, u3Buffer);
253        if (u3Buffer.compare(w3Buffer) != 0) {
254            UnicodeString locale(localeID);
255
256            log->errln("int32_t format error for locale " + locale +
257                                    ": got " + u3Buffer + " expected " + w3Buffer);
258        }
259
260        wnf->format(i64, u6Buffer);
261        if (u6Buffer.compare(w6Buffer) != 0) {
262            UnicodeString locale(localeID);
263
264            log->errln("int64_t format error for locale " + locale +
265                                    ": got " + u6Buffer + " expected " + w6Buffer);
266        }
267    }
268}
269
270void Win32NumberTest::testLocales(TestLog *log)
271{
272    int32_t lcidCount = 0;
273    Win32Utilities::LCIDRecord *lcidRecords = Win32Utilities::getLocales(lcidCount);
274
275    for(int i = 0; i < lcidCount; i += 1) {
276        UErrorCode status = U_ZERO_ERROR;
277        char localeID[128];
278
279        // NULL localeID means ICU didn't recognize the lcid
280        if (lcidRecords[i].localeID == NULL) {
281            continue;
282        }
283
284        strcpy(localeID, lcidRecords[i].localeID);
285
286        if (strchr(localeID, '@') > 0) {
287            strcat(localeID, ";");
288        } else {
289            strcat(localeID, "@");
290        }
291
292        strcat(localeID, "compat=host");
293
294        Locale ulocale(localeID);
295        NumberFormat *wnf = NumberFormat::createInstance(ulocale, status);
296        NumberFormat *wcf = NumberFormat::createCurrencyInstance(ulocale, status);
297
298        testLocale(lcidRecords[i].localeID, lcidRecords[i].lcid, wnf, FALSE, log);
299        testLocale(lcidRecords[i].localeID, lcidRecords[i].lcid, wcf, TRUE,  log);
300
301#if 0
302        char *old_locale = strdup(setlocale(LC_ALL, NULL));
303
304        setlocale(LC_ALL, "German");
305
306        testLocale(lcidRecords[i].localeID, lcidRecords[i].lcid, wnf, FALSE, log);
307        testLocale(lcidRecords[i].localeID, lcidRecords[i].lcid, wcf, TRUE,  log);
308
309        setlocale(LC_ALL, old_locale);
310
311        free(old_locale);
312#endif
313
314        delete wcf;
315        delete wnf;
316    }
317
318    Win32Utilities::freeLocales(lcidRecords);
319}
320
321#endif /* #if !UCONFIG_NO_FORMATTING */
322
323#endif /* #ifdef U_WINDOWS */
324