1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 ********************************************************************************
5 * Copyright (C) 2005-2016, International Business Machines
6 * Corporation and others. All Rights Reserved.
7 ********************************************************************************
11 ********************************************************************************
14 #include "unicode/utypes.h"
16 #if U_PLATFORM_USES_ONLY_WIN32_API
18 #if !UCONFIG_NO_FORMATTING
20 #include "unicode/format.h"
21 #include "unicode/numfmt.h"
22 #include "unicode/locid.h"
23 #include "unicode/ustring.h"
24 #include "unicode/testlog.h"
25 #include "unicode/utmscale.h"
39 # define WIN32_LEAN_AND_MEAN
51 #define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type))
52 #define DELETE_ARRAY(array) uprv_free((void *) (array))
54 #define STACK_BUFFER_SIZE 32
56 #define LOOP_COUNT 1000
58 static UBool initialized
= FALSE
;
61 * Return a random int64_t where U_INT64_MIN <= ran <= U_INT64_MAX.
63 static uint64_t randomInt64(void)
69 srand((unsigned)time(NULL
));
73 /* Assume rand has at least 12 bits of precision */
74 for (i
= 0; i
< sizeof(ran
); i
+= 1) {
75 ((char*)&ran
)[i
] = (char)((rand() & 0x0FF0) >> 4);
82 * Return a random double where U_DOUBLE_MIN <= ran <= U_DOUBLE_MAX.
84 static double randomDouble(void)
89 srand((unsigned)time(NULL
));
95 /* Assume rand has at least 12 bits of precision */
96 for (i
= 0; i
< sizeof(ran
); i
+= 1) {
97 ((char*)&ran
)[i
] = (char)((rand() & 0x0FF0) >> 4);
99 } while (_isnan(ran
));
101 int64_t numerator
= randomInt64();
104 denomenator
= randomInt64();
106 while (denomenator
== 0);
108 ran
= (double)numerator
/ (double)denomenator
;
115 * Return a random int32_t where U_INT32_MIN <= ran <= U_INT32_MAX.
117 static uint32_t randomInt32(void)
123 srand((unsigned)time(NULL
));
127 /* Assume rand has at least 12 bits of precision */
128 for (i
= 0; i
< sizeof(ran
); i
+= 1) {
129 ((char*)&ran
)[i
] = (char)((rand() & 0x0FF0) >> 4);
135 static UnicodeString
&getWindowsFormat(int32_t lcid
, UBool currency
, UnicodeString
&appendTo
, const wchar_t *fmt
, ...)
137 wchar_t nStackBuffer
[STACK_BUFFER_SIZE
];
138 wchar_t *nBuffer
= nStackBuffer
;
144 /* Due to the arguments causing a result to be <= 23 characters (+2 for NULL and minus),
145 we don't need to reallocate the buffer. */
147 result
= _vsnwprintf(nBuffer
, STACK_BUFFER_SIZE
, fmt
, args
);
150 /* Just to make sure of the above statement, we add this assert */
151 U_ASSERT(result
>=0);
152 // The following code is not used because _vscwprintf isn't available on MinGW at the moment.
157 newLength = _vscwprintf(fmt, args);
160 nBuffer = NEW_ARRAY(UChar, newLength + 1);
163 result = _vsnwprintf(nBuffer, newLength + 1, fmt, args);
168 // vswprintf is sensitive to the locale set by setlocale. For some locales
169 // it doesn't use "." as the decimal separator, which is what GetNumberFormatW
170 // and GetCurrencyFormatW both expect to see.
172 // To fix this, we scan over the string and replace the first non-digits, except
173 // for a leading "-", with a "."
175 // Note: (nBuffer[0] == L'-') will evaluate to 1 if there is a leading '-' in the
176 // number, and 0 otherwise.
177 for (wchar_t *p
= &nBuffer
[nBuffer
[0] == L
'-']; *p
!= L
'\0'; p
+= 1) {
178 if (*p
< L
'0' || *p
> L
'9') {
184 wchar_t stackBuffer
[STACK_BUFFER_SIZE
];
185 wchar_t *buffer
= stackBuffer
;
190 result
= GetCurrencyFormatW(lcid
, 0, nBuffer
, NULL
, buffer
, STACK_BUFFER_SIZE
);
193 DWORD lastError
= GetLastError();
195 if (lastError
== ERROR_INSUFFICIENT_BUFFER
) {
196 int newLength
= GetCurrencyFormatW(lcid
, 0, nBuffer
, NULL
, NULL
, 0);
198 buffer
= NEW_ARRAY(wchar_t, newLength
);
200 GetCurrencyFormatW(lcid
, 0, nBuffer
, NULL
, buffer
, newLength
);
204 result
= GetNumberFormatW(lcid
, 0, nBuffer
, NULL
, buffer
, STACK_BUFFER_SIZE
);
207 DWORD lastError
= GetLastError();
209 if (lastError
== ERROR_INSUFFICIENT_BUFFER
) {
210 int newLength
= GetNumberFormatW(lcid
, 0, nBuffer
, NULL
, NULL
, 0);
212 buffer
= NEW_ARRAY(wchar_t, newLength
);
214 GetNumberFormatW(lcid
, 0, nBuffer
, NULL
, buffer
, newLength
);
219 appendTo
.append((const UChar
*)buffer
, (int32_t) wcslen(buffer
));
221 if (buffer
!= stackBuffer
) {
222 DELETE_ARRAY(buffer
);
225 /*if (nBuffer != nStackBuffer) {
226 DELETE_ARRAY(nBuffer);
232 static void testLocale(const char *localeID
, int32_t lcid
, NumberFormat
*wnf
, UBool currency
, TestLog
*log
)
234 for (int n
= 0; n
< LOOP_COUNT
; n
+= 1) {
235 UnicodeString u3Buffer
, u6Buffer
, udBuffer
;
236 UnicodeString w3Buffer
, w6Buffer
, wdBuffer
;
237 double d
= randomDouble();
238 int32_t i32
= randomInt32();
239 int64_t i64
= randomInt64();
241 getWindowsFormat(lcid
, currency
, wdBuffer
, L
"%.16f", d
);
243 getWindowsFormat(lcid
, currency
, w3Buffer
, L
"%I32d", i32
);
245 getWindowsFormat(lcid
, currency
, w6Buffer
, L
"%I64d", i64
);
247 wnf
->format(d
, udBuffer
);
248 if (udBuffer
.compare(wdBuffer
) != 0) {
249 UnicodeString
locale(localeID
);
251 log
->errln("Double format error for locale " + locale
+
252 ": got " + udBuffer
+ " expected " + wdBuffer
);
255 wnf
->format(i32
, u3Buffer
);
256 if (u3Buffer
.compare(w3Buffer
) != 0) {
257 UnicodeString
locale(localeID
);
259 log
->errln("int32_t format error for locale " + locale
+
260 ": got " + u3Buffer
+ " expected " + w3Buffer
);
263 wnf
->format(i64
, u6Buffer
);
264 if (u6Buffer
.compare(w6Buffer
) != 0) {
265 UnicodeString
locale(localeID
);
267 log
->errln("int64_t format error for locale " + locale
+
268 ": got " + u6Buffer
+ " expected " + w6Buffer
);
273 void Win32NumberTest::testLocales(NumberFormatTest
*log
)
275 int32_t lcidCount
= 0;
276 Win32Utilities::LCIDRecord
*lcidRecords
= Win32Utilities::getLocales(lcidCount
);
278 for(int i
= 0; i
< lcidCount
; i
+= 1) {
279 UErrorCode status
= U_ZERO_ERROR
;
282 // NULL localeID means ICU didn't recognize the lcid
283 if (lcidRecords
[i
].localeID
== NULL
) {
287 // Some locales have had their names change over various OS releases; skip them in the test for now.
288 int32_t failingLocaleLCIDs
[] = {
289 0x040a, /* es-ES_tradnl;es-ES-u-co-trad; */
290 0x048c, /* fa-AF;prs-AF;prs-Arab-AF; */
291 0x046b, /* qu-BO;quz-BO;quz-Latn-BO; */
292 0x086b, /* qu-EC;quz-EC;quz-Latn-EC; */
293 0x0c6b, /* qu-PE;quz-PE;quz-Latn-PE; */
294 0x0492 /* ckb-IQ;ku-Arab-IQ; */
296 bool skip
= (std::find(std::begin(failingLocaleLCIDs
), std::end(failingLocaleLCIDs
), lcidRecords
[i
].lcid
) != std::end(failingLocaleLCIDs
));
297 if (skip
&& log
->logKnownIssue("13119", "Windows '@compat=host' fails on down-level versions of the OS")) {
298 log
->logln("ticket:13119 - Skipping LCID = 0x%04x", lcidRecords
[i
].lcid
);
302 strcpy(localeID
, lcidRecords
[i
].localeID
);
304 if (strchr(localeID
, '@') > 0) {
305 strcat(localeID
, ";");
307 strcat(localeID
, "@");
310 strcat(localeID
, "compat=host");
312 Locale
ulocale(localeID
);
313 NumberFormat
*wnf
= NumberFormat::createInstance(ulocale
, status
);
314 NumberFormat
*wcf
= NumberFormat::createCurrencyInstance(ulocale
, status
);
316 testLocale(lcidRecords
[i
].localeID
, lcidRecords
[i
].lcid
, wnf
, FALSE
, log
);
317 testLocale(lcidRecords
[i
].localeID
, lcidRecords
[i
].lcid
, wcf
, TRUE
, log
);
320 char *old_locale
= strdup(setlocale(LC_ALL
, NULL
));
322 setlocale(LC_ALL
, "German");
324 testLocale(lcidRecords
[i
].localeID
, lcidRecords
[i
].lcid
, wnf
, FALSE
, log
);
325 testLocale(lcidRecords
[i
].localeID
, lcidRecords
[i
].lcid
, wcf
, TRUE
, log
);
327 setlocale(LC_ALL
, old_locale
);
336 Win32Utilities::freeLocales(lcidRecords
);
339 #endif /* #if !UCONFIG_NO_FORMATTING */
341 #endif /* U_PLATFORM_USES_ONLY_WIN32_API */