]> git.saurik.com Git - apple/icu.git/blame - icuSources/test/intltest/winnmtst.cpp
ICU-64260.0.1.tar.gz
[apple/icu.git] / icuSources / test / intltest / winnmtst.cpp
CommitLineData
f3c0d7a5
A
1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
73c04bcf
A
3/*
4********************************************************************************
2ca993e8 5* Copyright (C) 2005-2016, International Business Machines
73c04bcf
A
6* Corporation and others. All Rights Reserved.
7********************************************************************************
8*
9* File WINNMTST.CPP
10*
11********************************************************************************
12*/
13
14#include "unicode/utypes.h"
15
4388f060 16#if U_PLATFORM_USES_ONLY_WIN32_API
73c04bcf
A
17
18#if !UCONFIG_NO_FORMATTING
19
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"
26
27#include "winnmfmt.h"
28#include "winutil.h"
29#include "winnmtst.h"
30
f3c0d7a5
A
31#include "numfmtst.h"
32
73c04bcf
A
33#include "cmemory.h"
34#include "cstring.h"
35#include "locmap.h"
36#include "wintz.h"
37#include "uassert.h"
38
39# define WIN32_LEAN_AND_MEAN
40# define VC_EXTRALEAN
41# define NOUSER
42# define NOSERVICE
43# define NOIME
44# define NOMCX
45# include <windows.h>
46# include <stdio.h>
47# include <time.h>
48# include <float.h>
49# include <locale.h>
50
0f5d89e8
A
51#include <algorithm>
52
73c04bcf
A
53#define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type))
54#define DELETE_ARRAY(array) uprv_free((void *) (array))
55
56#define STACK_BUFFER_SIZE 32
57
58#define LOOP_COUNT 1000
59
60static UBool initialized = FALSE;
61
62/**
63 * Return a random int64_t where U_INT64_MIN <= ran <= U_INT64_MAX.
64 */
65static uint64_t randomInt64(void)
66{
67 int64_t ran = 0;
68 int32_t i;
69
70 if (!initialized) {
71 srand((unsigned)time(NULL));
72 initialized = TRUE;
73 }
74
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);
78 }
79
80 return ran;
81}
82
83/**
84 * Return a random double where U_DOUBLE_MIN <= ran <= U_DOUBLE_MAX.
85 */
86static double randomDouble(void)
87{
88 double ran = 0;
89
90 if (!initialized) {
91 srand((unsigned)time(NULL));
92 initialized = TRUE;
93 }
94#if 0
95 int32_t i;
96 do {
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);
100 }
101 } while (_isnan(ran));
102#else
103 int64_t numerator = randomInt64();
104 int64_t denomenator;
105 do {
106 denomenator = randomInt64();
107 }
108 while (denomenator == 0);
109
110 ran = (double)numerator / (double)denomenator;
111#endif
112
113 return ran;
114}
115
116/**
117 * Return a random int32_t where U_INT32_MIN <= ran <= U_INT32_MAX.
118 */
119static uint32_t randomInt32(void)
120{
121 int32_t ran = 0;
122 int32_t i;
123
124 if (!initialized) {
125 srand((unsigned)time(NULL));
126 initialized = TRUE;
127 }
128
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);
132 }
133
134 return ran;
135}
136
137static UnicodeString &getWindowsFormat(int32_t lcid, UBool currency, UnicodeString &appendTo, const wchar_t *fmt, ...)
138{
139 wchar_t nStackBuffer[STACK_BUFFER_SIZE];
140 wchar_t *nBuffer = nStackBuffer;
141 va_list args;
142 int result;
143
144 nBuffer[0] = 0x0000;
145
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. */
148 va_start(args, fmt);
149 result = _vsnwprintf(nBuffer, STACK_BUFFER_SIZE, fmt, args);
150 va_end(args);
151
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.
155 /*if (result < 0) {
156 int newLength;
157
158 va_start(args, fmt);
159 newLength = _vscwprintf(fmt, args);
160 va_end(args);
161
162 nBuffer = NEW_ARRAY(UChar, newLength + 1);
163
164 va_start(args, fmt);
165 result = _vsnwprintf(nBuffer, newLength + 1, fmt, args);
166 va_end(args);
167 }*/
168
169
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.
173 //
174 // To fix this, we scan over the string and replace the first non-digits, except
175 // for a leading "-", with a "."
176 //
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') {
181 *p = L'.';
182 break;
183 }
184 }
185
186 wchar_t stackBuffer[STACK_BUFFER_SIZE];
187 wchar_t *buffer = stackBuffer;
188
189 buffer[0] = 0x0000;
190
191 if (currency) {
192 result = GetCurrencyFormatW(lcid, 0, nBuffer, NULL, buffer, STACK_BUFFER_SIZE);
193
194 if (result == 0) {
195 DWORD lastError = GetLastError();
196
197 if (lastError == ERROR_INSUFFICIENT_BUFFER) {
198 int newLength = GetCurrencyFormatW(lcid, 0, nBuffer, NULL, NULL, 0);
199
f3c0d7a5 200 buffer = NEW_ARRAY(wchar_t, newLength);
73c04bcf
A
201 buffer[0] = 0x0000;
202 GetCurrencyFormatW(lcid, 0, nBuffer, NULL, buffer, newLength);
203 }
204 }
205 } else {
206 result = GetNumberFormatW(lcid, 0, nBuffer, NULL, buffer, STACK_BUFFER_SIZE);
207
208 if (result == 0) {
209 DWORD lastError = GetLastError();
210
211 if (lastError == ERROR_INSUFFICIENT_BUFFER) {
212 int newLength = GetNumberFormatW(lcid, 0, nBuffer, NULL, NULL, 0);
213
f3c0d7a5 214 buffer = NEW_ARRAY(wchar_t, newLength);
73c04bcf
A
215 buffer[0] = 0x0000;
216 GetNumberFormatW(lcid, 0, nBuffer, NULL, buffer, newLength);
217 }
218 }
219 }
220
f3c0d7a5 221 appendTo.append((const UChar *)buffer, (int32_t) wcslen(buffer));
73c04bcf
A
222
223 if (buffer != stackBuffer) {
224 DELETE_ARRAY(buffer);
225 }
226
227 /*if (nBuffer != nStackBuffer) {
228 DELETE_ARRAY(nBuffer);
229 }*/
230
231 return appendTo;
232}
233
234static void testLocale(const char *localeID, int32_t lcid, NumberFormat *wnf, UBool currency, TestLog *log)
235{
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();
242
243 getWindowsFormat(lcid, currency, wdBuffer, L"%.16f", d);
244
245 getWindowsFormat(lcid, currency, w3Buffer, L"%I32d", i32);
246
247 getWindowsFormat(lcid, currency, w6Buffer, L"%I64d", i64);
248
249 wnf->format(d, udBuffer);
250 if (udBuffer.compare(wdBuffer) != 0) {
251 UnicodeString locale(localeID);
252
253 log->errln("Double format error for locale " + locale +
729e4ab9 254 ": got " + udBuffer + " expected " + wdBuffer);
73c04bcf
A
255 }
256
257 wnf->format(i32, u3Buffer);
258 if (u3Buffer.compare(w3Buffer) != 0) {
259 UnicodeString locale(localeID);
260
261 log->errln("int32_t format error for locale " + locale +
729e4ab9 262 ": got " + u3Buffer + " expected " + w3Buffer);
73c04bcf
A
263 }
264
265 wnf->format(i64, u6Buffer);
266 if (u6Buffer.compare(w6Buffer) != 0) {
267 UnicodeString locale(localeID);
268
269 log->errln("int64_t format error for locale " + locale +
729e4ab9 270 ": got " + u6Buffer + " expected " + w6Buffer);
73c04bcf
A
271 }
272 }
273}
274
f3c0d7a5 275void Win32NumberTest::testLocales(NumberFormatTest *log)
73c04bcf
A
276{
277 int32_t lcidCount = 0;
278 Win32Utilities::LCIDRecord *lcidRecords = Win32Utilities::getLocales(lcidCount);
279
280 for(int i = 0; i < lcidCount; i += 1) {
281 UErrorCode status = U_ZERO_ERROR;
282 char localeID[128];
283
729e4ab9
A
284 // NULL localeID means ICU didn't recognize the lcid
285 if (lcidRecords[i].localeID == NULL) {
286 continue;
287 }
73c04bcf 288
f3c0d7a5
A
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; */
297 };
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);
301 continue;
302 }
303
73c04bcf
A
304 strcpy(localeID, lcidRecords[i].localeID);
305
306 if (strchr(localeID, '@') > 0) {
307 strcat(localeID, ";");
308 } else {
309 strcat(localeID, "@");
310 }
311
312 strcat(localeID, "compat=host");
313
314 Locale ulocale(localeID);
315 NumberFormat *wnf = NumberFormat::createInstance(ulocale, status);
316 NumberFormat *wcf = NumberFormat::createCurrencyInstance(ulocale, status);
317
318 testLocale(lcidRecords[i].localeID, lcidRecords[i].lcid, wnf, FALSE, log);
319 testLocale(lcidRecords[i].localeID, lcidRecords[i].lcid, wcf, TRUE, log);
320
321#if 0
322 char *old_locale = strdup(setlocale(LC_ALL, NULL));
323
324 setlocale(LC_ALL, "German");
325
326 testLocale(lcidRecords[i].localeID, lcidRecords[i].lcid, wnf, FALSE, log);
327 testLocale(lcidRecords[i].localeID, lcidRecords[i].lcid, wcf, TRUE, log);
328
329 setlocale(LC_ALL, old_locale);
330
331 free(old_locale);
332#endif
333
334 delete wcf;
335 delete wnf;
336 }
337
338 Win32Utilities::freeLocales(lcidRecords);
339}
340
341#endif /* #if !UCONFIG_NO_FORMATTING */
342
4388f060 343#endif /* U_PLATFORM_USES_ONLY_WIN32_API */