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
53 #define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type))
54 #define DELETE_ARRAY(array) uprv_free((void *) (array))
56 #define STACK_BUFFER_SIZE 32
58 #define LOOP_COUNT 1000
60 static UBool initialized
= FALSE
;
63 * Return a random int64_t where U_INT64_MIN <= ran <= U_INT64_MAX.
65 static uint64_t randomInt64(void)
71 srand((unsigned)time(NULL
));
75 /* Assume rand has at least 12 bits of precision */
76 for (i
= 0; i
< sizeof(ran
); i
+= 1) {
77 ((char*)&ran
)[i
] = (char)((rand() & 0x0FF0) >> 4);
84 * Return a random double where U_DOUBLE_MIN <= ran <= U_DOUBLE_MAX.
86 static double randomDouble(void)
91 srand((unsigned)time(NULL
));
97 /* Assume rand has at least 12 bits of precision */
98 for (i
= 0; i
< sizeof(ran
); i
+= 1) {
99 ((char*)&ran
)[i
] = (char)((rand() & 0x0FF0) >> 4);
101 } while (_isnan(ran
));
103 int64_t numerator
= randomInt64();
106 denomenator
= randomInt64();
108 while (denomenator
== 0);
110 ran
= (double)numerator
/ (double)denomenator
;
117 * Return a random int32_t where U_INT32_MIN <= ran <= U_INT32_MAX.
119 static uint32_t randomInt32(void)
125 srand((unsigned)time(NULL
));
129 /* Assume rand has at least 12 bits of precision */
130 for (i
= 0; i
< sizeof(ran
); i
+= 1) {
131 ((char*)&ran
)[i
] = (char)((rand() & 0x0FF0) >> 4);
137 static UnicodeString
&getWindowsFormat(int32_t lcid
, UBool currency
, UnicodeString
&appendTo
, const wchar_t *fmt
, ...)
139 wchar_t nStackBuffer
[STACK_BUFFER_SIZE
];
140 wchar_t *nBuffer
= nStackBuffer
;
146 /* Due to the arguments causing a result to be <= 23 characters (+2 for NULL and minus),
147 we don't need to reallocate the buffer. */
149 result
= _vsnwprintf(nBuffer
, STACK_BUFFER_SIZE
, fmt
, args
);
152 /* Just to make sure of the above statement, we add this assert */
153 U_ASSERT(result
>=0);
154 // The following code is not used because _vscwprintf isn't available on MinGW at the moment.
159 newLength = _vscwprintf(fmt, args);
162 nBuffer = NEW_ARRAY(UChar, newLength + 1);
165 result = _vsnwprintf(nBuffer, newLength + 1, fmt, args);
170 // vswprintf is sensitive to the locale set by setlocale. For some locales
171 // it doesn't use "." as the decimal separator, which is what GetNumberFormatW
172 // and GetCurrencyFormatW both expect to see.
174 // To fix this, we scan over the string and replace the first non-digits, except
175 // for a leading "-", with a "."
177 // Note: (nBuffer[0] == L'-') will evaluate to 1 if there is a leading '-' in the
178 // number, and 0 otherwise.
179 for (wchar_t *p
= &nBuffer
[nBuffer
[0] == L
'-']; *p
!= L
'\0'; p
+= 1) {
180 if (*p
< L
'0' || *p
> L
'9') {
186 wchar_t stackBuffer
[STACK_BUFFER_SIZE
];
187 wchar_t *buffer
= stackBuffer
;
192 result
= GetCurrencyFormatW(lcid
, 0, nBuffer
, NULL
, buffer
, STACK_BUFFER_SIZE
);
195 DWORD lastError
= GetLastError();
197 if (lastError
== ERROR_INSUFFICIENT_BUFFER
) {
198 int newLength
= GetCurrencyFormatW(lcid
, 0, nBuffer
, NULL
, NULL
, 0);
200 buffer
= NEW_ARRAY(wchar_t, newLength
);
202 GetCurrencyFormatW(lcid
, 0, nBuffer
, NULL
, buffer
, newLength
);
206 result
= GetNumberFormatW(lcid
, 0, nBuffer
, NULL
, buffer
, STACK_BUFFER_SIZE
);
209 DWORD lastError
= GetLastError();
211 if (lastError
== ERROR_INSUFFICIENT_BUFFER
) {
212 int newLength
= GetNumberFormatW(lcid
, 0, nBuffer
, NULL
, NULL
, 0);
214 buffer
= NEW_ARRAY(wchar_t, newLength
);
216 GetNumberFormatW(lcid
, 0, nBuffer
, NULL
, buffer
, newLength
);
221 appendTo
.append((const UChar
*)buffer
, (int32_t) wcslen(buffer
));
223 if (buffer
!= stackBuffer
) {
224 DELETE_ARRAY(buffer
);
227 /*if (nBuffer != nStackBuffer) {
228 DELETE_ARRAY(nBuffer);
234 static void testLocale(const char *localeID
, int32_t lcid
, NumberFormat
*wnf
, UBool currency
, TestLog
*log
)
236 for (int n
= 0; n
< LOOP_COUNT
; n
+= 1) {
237 UnicodeString u3Buffer
, u6Buffer
, udBuffer
;
238 UnicodeString w3Buffer
, w6Buffer
, wdBuffer
;
239 double d
= randomDouble();
240 int32_t i32
= randomInt32();
241 int64_t i64
= randomInt64();
243 getWindowsFormat(lcid
, currency
, wdBuffer
, L
"%.16f", d
);
245 getWindowsFormat(lcid
, currency
, w3Buffer
, L
"%I32d", i32
);
247 getWindowsFormat(lcid
, currency
, w6Buffer
, L
"%I64d", i64
);
249 wnf
->format(d
, udBuffer
);
250 if (udBuffer
.compare(wdBuffer
) != 0) {
251 UnicodeString
locale(localeID
);
253 log
->errln("Double format error for locale " + locale
+
254 ": got " + udBuffer
+ " expected " + wdBuffer
);
257 wnf
->format(i32
, u3Buffer
);
258 if (u3Buffer
.compare(w3Buffer
) != 0) {
259 UnicodeString
locale(localeID
);
261 log
->errln("int32_t format error for locale " + locale
+
262 ": got " + u3Buffer
+ " expected " + w3Buffer
);
265 wnf
->format(i64
, u6Buffer
);
266 if (u6Buffer
.compare(w6Buffer
) != 0) {
267 UnicodeString
locale(localeID
);
269 log
->errln("int64_t format error for locale " + locale
+
270 ": got " + u6Buffer
+ " expected " + w6Buffer
);
275 void Win32NumberTest::testLocales(NumberFormatTest
*log
)
277 int32_t lcidCount
= 0;
278 Win32Utilities::LCIDRecord
*lcidRecords
= Win32Utilities::getLocales(lcidCount
);
280 for(int i
= 0; i
< lcidCount
; i
+= 1) {
281 UErrorCode status
= U_ZERO_ERROR
;
284 // NULL localeID means ICU didn't recognize the lcid
285 if (lcidRecords
[i
].localeID
== NULL
) {
289 // Some locales have had their names change over various OS releases; skip them in the test for now.
290 int32_t failingLocaleLCIDs
[] = {
291 0x040a, /* es-ES_tradnl;es-ES-u-co-trad; */
292 0x048c, /* fa-AF;prs-AF;prs-Arab-AF; */
293 0x046b, /* qu-BO;quz-BO;quz-Latn-BO; */
294 0x086b, /* qu-EC;quz-EC;quz-Latn-EC; */
295 0x0c6b, /* qu-PE;quz-PE;quz-Latn-PE; */
296 0x0492 /* ckb-IQ;ku-Arab-IQ; */
298 bool skip
= (std::find(std::begin(failingLocaleLCIDs
), std::end(failingLocaleLCIDs
), lcidRecords
[i
].lcid
) != std::end(failingLocaleLCIDs
));
299 if (skip
&& log
->logKnownIssue("13119", "Windows '@compat=host' fails on down-level versions of the OS")) {
300 log
->logln("ticket:13119 - Skipping LCID = 0x%04x", lcidRecords
[i
].lcid
);
304 strcpy(localeID
, lcidRecords
[i
].localeID
);
306 if (strchr(localeID
, '@') > 0) {
307 strcat(localeID
, ";");
309 strcat(localeID
, "@");
312 strcat(localeID
, "compat=host");
314 Locale
ulocale(localeID
);
315 NumberFormat
*wnf
= NumberFormat::createInstance(ulocale
, status
);
316 NumberFormat
*wcf
= NumberFormat::createCurrencyInstance(ulocale
, status
);
318 testLocale(lcidRecords
[i
].localeID
, lcidRecords
[i
].lcid
, wnf
, FALSE
, log
);
319 testLocale(lcidRecords
[i
].localeID
, lcidRecords
[i
].lcid
, wcf
, TRUE
, log
);
322 char *old_locale
= strdup(setlocale(LC_ALL
, NULL
));
324 setlocale(LC_ALL
, "German");
326 testLocale(lcidRecords
[i
].localeID
, lcidRecords
[i
].lcid
, wnf
, FALSE
, log
);
327 testLocale(lcidRecords
[i
].localeID
, lcidRecords
[i
].lcid
, wcf
, TRUE
, log
);
329 setlocale(LC_ALL
, old_locale
);
338 Win32Utilities::freeLocales(lcidRecords
);
341 #endif /* #if !UCONFIG_NO_FORMATTING */
343 #endif /* U_PLATFORM_USES_ONLY_WIN32_API */