1 /*
2  * VARFORMAT test program
3  *
4  * Copyright 1998 Jean-Claude Cote
5  * Copyright 2006 Google (Benjamin Arai)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <math.h>
25 #include <float.h>
26 
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winsock2.h"
30 #include "wine/test.h"
31 #include "winuser.h"
32 #include "wingdi.h"
33 #include "winnls.h"
34 #include "winerror.h"
35 #include "winnt.h"
36 
37 #include "wtypes.h"
38 #include "oleauto.h"
39 
40 static HMODULE hOleaut32;
41 
42 static HRESULT (WINAPI *pVarBstrCmp)(BSTR,BSTR,LCID,ULONG);
43 static HRESULT (WINAPI *pVarFormatNumber)(LPVARIANT,int,int,int,int,ULONG,BSTR*);
44 static HRESULT (WINAPI *pVarFormat)(LPVARIANT,LPOLESTR,int,int,ULONG,BSTR*);
45 static HRESULT (WINAPI *pVarWeekdayName)(int,int,int,ULONG,BSTR*);
46 
47 /* Has I8/UI8 data type? */
48 static BOOL has_i8;
49 
50 /* Get a conversion function ptr, return if function not available */
51 #define CHECKPTR(func) p##func = (void*)GetProcAddress(hOleaut32, #func); \
52   if (!p##func) { win_skip("function " # func " not available, not testing it\n"); return; }
53 
strcmpW(const WCHAR * str1,const WCHAR * str2)54 static inline int strcmpW( const WCHAR *str1, const WCHAR *str2 )
55 {
56     while (*str1 && (*str1 == *str2)) { str1++; str2++; }
57     return *str1 - *str2;
58 }
59 
60 #define FMT_NUMBER(vt,val) \
61   VariantInit(&v); V_VT(&v) = vt; val(&v) = 1; \
62   hres = pVarFormatNumber(&v,2,0,0,0,0,&str); \
63   ok(hres == S_OK, "VarFormatNumber (vt %d): returned %8x\n", vt, hres); \
64   if (hres == S_OK) { \
65     ok(str && strcmpW(str,szResult1) == 0, \
66        "VarFormatNumber (vt %d): string different\n", vt); \
67     SysFreeString(str); \
68   }
69 
test_VarFormatNumber(void)70 static void test_VarFormatNumber(void)
71 {
72   static const WCHAR szSrc1[] = { '1','\0' };
73   static const WCHAR szResult1[] = { '1','.','0','0','\0' };
74   static const WCHAR szSrc2[] = { '-','1','\0' };
75   static const WCHAR szResult2[] = { '(','1','.','0','0',')','\0' };
76   char buff[8];
77   HRESULT hres;
78   VARIANT v;
79   BSTR str = NULL;
80 
81   CHECKPTR(VarFormatNumber);
82 
83   GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, buff, ARRAY_SIZE(buff));
84   if (buff[0] != '.' || buff[1])
85   {
86     skip("Skipping VarFormatNumber tests as decimal separator is '%s'\n", buff);
87     return;
88   }
89 
90   FMT_NUMBER(VT_I1, V_I1);
91   FMT_NUMBER(VT_UI1, V_UI1);
92   FMT_NUMBER(VT_I2, V_I2);
93   FMT_NUMBER(VT_UI2, V_UI2);
94   FMT_NUMBER(VT_I4, V_I4);
95   FMT_NUMBER(VT_UI4, V_UI4);
96   if (has_i8)
97   {
98     FMT_NUMBER(VT_I8, V_I8);
99     FMT_NUMBER(VT_UI8, V_UI8);
100   }
101   FMT_NUMBER(VT_R4, V_R4);
102   FMT_NUMBER(VT_R8, V_R8);
103   FMT_NUMBER(VT_BOOL, V_BOOL);
104 
105   V_VT(&v) = VT_BSTR;
106   V_BSTR(&v) = SysAllocString(szSrc1);
107 
108   hres = pVarFormatNumber(&v,2,0,0,0,0,&str);
109   ok(hres == S_OK, "VarFormatNumber (bstr): returned %8x\n", hres);
110   if (hres == S_OK)
111     ok(str && strcmpW(str, szResult1) == 0, "VarFormatNumber (bstr): string different\n");
112   SysFreeString(V_BSTR(&v));
113   SysFreeString(str);
114 
115   V_BSTR(&v) = SysAllocString(szSrc2);
116   hres = pVarFormatNumber(&v,2,0,-1,0,0,&str);
117   ok(hres == S_OK, "VarFormatNumber (bstr): returned %8x\n", hres);
118   if (hres == S_OK)
119     ok(str && strcmpW(str, szResult2) == 0, "VarFormatNumber (-bstr): string different\n");
120   SysFreeString(V_BSTR(&v));
121   SysFreeString(str);
122 }
123 
124 #define SIGNED_VTBITS (VTBIT_I1|VTBIT_I2|VTBIT_I4|VTBIT_I8|VTBIT_R4|VTBIT_R8)
125 
126 static const char *szVarFmtFail = "VT %d|0x%04x Format %s: expected 0x%08x, '%s', got 0x%08x, '%s'\n";
127 #define VARFMT(vt,v,val,fmt,ret,str) do { \
128   out = NULL; \
129   V_VT(&in) = (vt); v(&in) = val; \
130   if (fmt) MultiByteToWideChar(CP_ACP, 0, fmt, -1, buffW, ARRAY_SIZE(buffW)); \
131   hres = pVarFormat(&in,fmt ? buffW : NULL,fd,fw,flags,&out); \
132   if (SUCCEEDED(hres)) WideCharToMultiByte(CP_ACP, 0, out, -1, buff, sizeof(buff),0,0); \
133   else buff[0] = '\0'; \
134   ok(hres == ret && (FAILED(ret) || !strcmp(buff, str)), \
135      szVarFmtFail, \
136      (vt)&VT_TYPEMASK,(vt)&~VT_TYPEMASK,fmt?fmt:"<null>",ret,str,hres,buff); \
137   SysFreeString(out); \
138   } while(0)
139 
140 typedef struct tagFMTRES
141 {
142   LPCSTR fmt;
143   LPCSTR one_res;
144   LPCSTR zero_res;
145 } FMTRES;
146 
147 static const FMTRES VarFormat_results[] =
148 {
149   { NULL, "1", "0" },
150   { "", "1", "0" },
151   { "General Number", "1", "0" },
152   { "Percent", "100.00%", "0.00%" },
153   { "Standard", "1.00", "0.00" },
154   { "Scientific","1.00E+00", "0.00E+00" },
155   { "True/False", "True", "False" },
156   { "On/Off", "On", "Off" },
157   { "Yes/No", "Yes", "No" },
158   { "#", "1", "" },
159   { "##", "1", "" },
160   { "#.#", "1.", "." },
161   { "0", "1", "0" },
162   { "00", "01", "00" },
163   { "0.0", "1.0", "0.0" },
164   { "00\\c\\o\\p\\y", "01copy","00copy" },
165   { "\"pos\";\"neg\"", "pos", "pos" },
166   { "\"pos\";\"neg\";\"zero\"","pos", "zero" }
167 };
168 
169 typedef struct tagFMTDATERES
170 {
171   DATE   val;
172   LPCSTR fmt;
173   LPCSTR res;
174 } FMTDATERES;
175 
176 static const FMTDATERES VarFormat_date_results[] =
177 {
178   { 0.0, "w", "7" },
179   { 0.0, "w", "6" },
180   { 0.0, "w", "5" },
181   { 0.0, "w", "4" },
182   { 0.0, "w", "3" },
183   { 0.0, "w", "2" },
184   { 0.0, "w", "1" }, /* First 7 entries must remain in this order! */
185   { 2.525, "am/pm", "pm" },
186   { 2.525, "AM/PM", "PM" },
187   { 2.525, "A/P", "P" },
188   { 2.525, "a/p", "p" },
189   { 2.525, "q", "1" },
190   { 2.525, "d", "1" },
191   { 2.525, "dd", "01" },
192   { 2.525, "ddd", "Mon" },
193   { 2.525, "dddd", "Monday" },
194   { 2.525, "mmm", "Jan" },
195   { 2.525, "mmmm", "January" },
196   { 2.525, "y", "1" },
197   { 2.525, "yy", "00" },
198   { 2.525, "yyy", "001" },
199   { 2.525, "yyyy", "1900" },
200   { 2.525, "dd mm yyyy hh:mm:ss", "01 01 1900 12:36:00" },
201   { 2.525, "dd mm yyyy mm", "01 01 1900 01" },
202   { 2.525, "dd mm yyyy :mm", "01 01 1900 :01" },
203   { 2.525, "dd mm yyyy hh:mm", "01 01 1900 12:36" },
204   { 2.525, "mm mm", "01 01" },
205   { 2.525, "mm :mm:ss", "01 :01:00" },
206   { 2.525, "mm :ss:mm", "01 :00:01" },
207   { 2.525, "hh:mm :ss:mm", "12:36 :00:01" },
208   { 2.525, "hh:dd :mm:mm", "12:01 :01:01" },
209   { 2.525, "dd:hh :mm:mm", "01:12 :36:01" },
210   { 2.525, "hh :mm:mm", "12 :36:01" },
211   { 2.525, "dd :mm:mm", "01 :01:01" },
212   { 2.525, "dd :mm:nn", "01 :01:36" },
213   { 2.725, "hh:nn:ss A/P", "05:24:00 P" },
214   { 40531.0, "dddd", "Sunday" },
215   { 40531.0, "ddd", "Sun" }
216 };
217 
218 /* The following tests require that the time separator is a colon (:) */
219 static const FMTDATERES VarFormat_namedtime_results[] =
220 {
221   { 2.525, "short time", "12:36" },
222   { 2.525, "medium time", "12:36 PM" },
223   { 2.525, "long time", "12:36:00 PM" }
224 };
225 
226 #define VNUMFMT(vt,v) \
227   for (i = 0; i < ARRAY_SIZE(VarFormat_results); i++) \
228   { \
229     VARFMT(vt,v,1,VarFormat_results[i].fmt,S_OK,VarFormat_results[i].one_res); \
230     VARFMT(vt,v,0,VarFormat_results[i].fmt,S_OK,VarFormat_results[i].zero_res); \
231   } \
232   if ((1 << vt) & SIGNED_VTBITS) \
233   { \
234     VARFMT(vt,v,-1,"\"pos\";\"neg\"",S_OK,"neg"); \
235     VARFMT(vt,v,-1,"\"pos\";\"neg\";\"zero\"",S_OK,"neg"); \
236   }
237 
test_VarFormat(void)238 static void test_VarFormat(void)
239 {
240   static const WCHAR szTesting[] = { 't','e','s','t','i','n','g','\0' };
241   static const WCHAR szNum[] = { '3','9','6','9','7','.','1','1','\0' };
242   size_t i;
243   WCHAR buffW[256];
244   char buff[256];
245   VARIANT in;
246   VARIANT_BOOL bTrue = VARIANT_TRUE, bFalse = VARIANT_FALSE;
247   int fd = 0, fw = 0;
248   ULONG flags = 0;
249   BSTR bstrin, out = NULL;
250   HRESULT hres;
251 
252   CHECKPTR(VarFormat);
253 
254   if (PRIMARYLANGID(LANGIDFROMLCID(GetUserDefaultLCID())) != LANG_ENGLISH)
255   {
256     skip("Skipping VarFormat tests for non English language\n");
257     return;
258   }
259   GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, buff, ARRAY_SIZE(buff));
260   if (buff[0] != '.' || buff[1])
261   {
262     skip("Skipping VarFormat tests as decimal separator is '%s'\n", buff);
263     return;
264   }
265   GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, buff, ARRAY_SIZE(buff));
266   if (buff[0] != '2' || buff[1])
267   {
268     skip("Skipping VarFormat tests as decimal places is '%s'\n", buff);
269     return;
270   }
271 
272   VARFMT(VT_BOOL,V_BOOL,VARIANT_TRUE,"True/False",S_OK,"True");
273   VARFMT(VT_BOOL,V_BOOL,VARIANT_FALSE,"True/False",S_OK,"False");
274 
275   VNUMFMT(VT_I1,V_I1);
276   VNUMFMT(VT_I2,V_I2);
277   VNUMFMT(VT_I4,V_I4);
278   if (has_i8)
279   {
280     VNUMFMT(VT_I8,V_I8);
281   }
282   VNUMFMT(VT_INT,V_INT);
283   VNUMFMT(VT_UI1,V_UI1);
284   VNUMFMT(VT_UI2,V_UI2);
285   VNUMFMT(VT_UI4,V_UI4);
286   if (has_i8)
287   {
288     VNUMFMT(VT_UI8,V_UI8);
289   }
290   VNUMFMT(VT_UINT,V_UINT);
291   VNUMFMT(VT_R4,V_R4);
292   VNUMFMT(VT_R8,V_R8);
293 
294   /* Reference types are dereferenced */
295   VARFMT(VT_BOOL|VT_BYREF,V_BOOLREF,&bTrue,"True/False",S_OK,"True");
296   VARFMT(VT_BOOL|VT_BYREF,V_BOOLREF,&bFalse,"True/False",S_OK,"False");
297 
298   /* Dates */
299   for (i = 0; i < ARRAY_SIZE(VarFormat_date_results); i++)
300   {
301     if (i < 7)
302       fd = i + 1; /* Test first day */
303     else
304       fd = 0;
305     VARFMT(VT_DATE,V_DATE,VarFormat_date_results[i].val,
306            VarFormat_date_results[i].fmt,S_OK,
307            VarFormat_date_results[i].res);
308   }
309 
310   /* Named time formats */
311   GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_STIMEFORMAT, buff, ARRAY_SIZE(buff));
312   if (strcmp(buff, "h:mm:ss tt"))
313   {
314     skip("Skipping named time tests as time format is '%s'\n", buff);
315   }
316   else
317   {
318     for (i = 0; i < ARRAY_SIZE(VarFormat_namedtime_results); i++)
319     {
320       fd = 0;
321       VARFMT(VT_DATE,V_DATE,VarFormat_namedtime_results[i].val,
322              VarFormat_namedtime_results[i].fmt,S_OK,
323              VarFormat_namedtime_results[i].res);
324     }
325   }
326 
327   /* Strings */
328   bstrin = SysAllocString(szTesting);
329   VARFMT(VT_BSTR,V_BSTR,bstrin,"",S_OK,"testing");
330   VARFMT(VT_BSTR,V_BSTR,bstrin,"@",S_OK,"testing");
331   VARFMT(VT_BSTR,V_BSTR,bstrin,"&",S_OK,"testing");
332   VARFMT(VT_BSTR,V_BSTR,bstrin,"\\x@\\x@",S_OK,"xtxesting");
333   VARFMT(VT_BSTR,V_BSTR,bstrin,"\\x&\\x&",S_OK,"xtxesting");
334   VARFMT(VT_BSTR,V_BSTR,bstrin,"@\\x",S_OK,"txesting");
335   VARFMT(VT_BSTR,V_BSTR,bstrin,"@@@@@@@@",S_OK," testing");
336   VARFMT(VT_BSTR,V_BSTR,bstrin,"@\\x@@@@@@@",S_OK," xtesting");
337   VARFMT(VT_BSTR,V_BSTR,bstrin,"&&&&&&&&",S_OK,"testing");
338   VARFMT(VT_BSTR,V_BSTR,bstrin,"!&&&&&&&",S_OK,"testing");
339   VARFMT(VT_BSTR,V_BSTR,bstrin,"&&&&&&&!",S_OK,"testing");
340   VARFMT(VT_BSTR,V_BSTR,bstrin,">&&",S_OK,"TESTING");
341   VARFMT(VT_BSTR,V_BSTR,bstrin,"<&&",S_OK,"testing");
342   VARFMT(VT_BSTR,V_BSTR,bstrin,"<&>&",S_OK,"testing");
343   SysFreeString(bstrin);
344   bstrin = SysAllocString(szNum);
345   todo_wine VARFMT(VT_BSTR,V_BSTR,bstrin,"hh:mm",S_OK,"02:38");
346   todo_wine VARFMT(VT_BSTR,V_BSTR,bstrin,"mm-dd-yy",S_OK,"09-06-08");
347   SysFreeString(bstrin);
348   /* Numeric values are converted to strings then output */
349   VARFMT(VT_I1,V_I1,1,"<&>&",S_OK,"1");
350 
351   /* Number formats */
352   VARFMT(VT_I4,V_I4,1,"#00000000",S_OK,"00000001");
353   VARFMT(VT_I4,V_I4,1,"000###",S_OK,"000001");
354   VARFMT(VT_I4,V_I4,1,"#00##00#0",S_OK,"00000001");
355   VARFMT(VT_I4,V_I4,1,"1#####0000",S_OK,"10001");
356   VARFMT(VT_I4,V_I4,1,"##abcdefghijklmnopqrstuvwxyz",S_OK,"1abcdefghijklmnopqrstuvwxyz");
357   VARFMT(VT_I4,V_I4,100000,"#,###,###,###",S_OK,"100,000");
358   VARFMT(VT_I4,V_I4,1,"0,000,000,000",S_OK,"0,000,000,001");
359   VARFMT(VT_I4,V_I4,123456789,"#,#.#",S_OK,"123,456,789.");
360   VARFMT(VT_I4,V_I4,123456789,"###, ###, ###",S_OK,"123, 456, 789");
361   VARFMT(VT_I4,V_I4,1,"#;-#",S_OK,"1");
362   VARFMT(VT_I4,V_I4,-1,"#;-#",S_OK,"-1");
363   VARFMT(VT_R8,V_R8,1.23456789,"0#.0#0#0#0#0",S_OK,"01.234567890");
364   VARFMT(VT_R8,V_R8,1.2,"0#.0#0#0#0#0",S_OK,"01.200000000");
365   VARFMT(VT_R8,V_R8,9.87654321,"#0.#0#0#0#0#",S_OK,"9.87654321");
366   VARFMT(VT_R8,V_R8,9.8,"#0.#0#0#0#0#",S_OK,"9.80000000");
367   VARFMT(VT_R8,V_R8,0.00000008,"#0.#0#0#0#0#0",S_OK,"0.0000000800");
368   VARFMT(VT_R8,V_R8,0.00010705,"#0.##########",S_OK,"0.00010705");
369   VARFMT(VT_I4,V_I4,17,"#0",S_OK,"17");
370   VARFMT(VT_I4,V_I4,4711,"#0",S_OK,"4711");
371   VARFMT(VT_I4,V_I4,17,"#00",S_OK,"17");
372   VARFMT(VT_I4,V_I4,100,"0##",S_OK,"100");
373   VARFMT(VT_I4,V_I4,17,"#000",S_OK,"017");
374   VARFMT(VT_I4,V_I4,17,"#0.00",S_OK,"17.00");
375   VARFMT(VT_I4,V_I4,17,"#0000.00",S_OK,"0017.00");
376   VARFMT(VT_I4,V_I4,17,"#.00",S_OK,"17.00");
377   VARFMT(VT_R8,V_R8,1.7,"#.00",S_OK,"1.70");
378   VARFMT(VT_R8,V_R8,.17,"#.00",S_OK,".17");
379   VARFMT(VT_I4,V_I4,17,"#3",S_OK,"173");
380   VARFMT(VT_I4,V_I4,17,"#33",S_OK,"1733");
381   VARFMT(VT_I4,V_I4,17,"#3.33",S_OK,"173.33");
382   VARFMT(VT_I4,V_I4,17,"#3333.33",S_OK,"173333.33");
383   VARFMT(VT_I4,V_I4,17,"#.33",S_OK,"17.33");
384   VARFMT(VT_R8,V_R8,.17,"#.33",S_OK,".33");
385   VARFMT(VT_R8,V_R8,1.7,"0.0000E-000",S_OK,"1.7000E000");
386   VARFMT(VT_R8,V_R8,1.7,"0.0000e-1",S_OK,"1.7000e01");
387   VARFMT(VT_R8,V_R8,86.936849,"#0.000000000000e-000",S_OK,"86.936849000000e000");
388   VARFMT(VT_R8,V_R8,1.7,"#0",S_OK,"2");
389   VARFMT(VT_R8,V_R8,1.7,"#.33",S_OK,"2.33");
390   VARFMT(VT_R8,V_R8,1.7,"#3",S_OK,"23");
391   VARFMT(VT_R8,V_R8,1.73245,"0.0000E+000",S_OK,"1.7325E+000");
392   VARFMT(VT_R8,V_R8,9.9999999,"#0.000000",S_OK,"10.000000");
393   VARFMT(VT_R8,V_R8,1.7,"0.0000e+0#",S_OK,"1.7000e+0");
394   VARFMT(VT_R8,V_R8,100.0001e+0,"0.0000E+0",S_OK,"1.0000E+2");
395   VARFMT(VT_R8,V_R8,1000001,"0.0000e+1",S_OK,"1.0000e+61");
396   VARFMT(VT_R8,V_R8,100.0001e+25,"0.0000e+0",S_OK,"1.0000e+27");
397   VARFMT(VT_R8,V_R8,450.0001e+43,"#000.0000e+0",S_OK,"4500.0010e+42");
398   VARFMT(VT_R8,V_R8,0.0001e-11,"##00.0000e-0",S_OK,"1000.0000e-18");
399   VARFMT(VT_R8,V_R8,0.0317e-11,"0000.0000e-0",S_OK,"3170.0000e-16");
400   VARFMT(VT_R8,V_R8,0.0021e-11,"00##.0000e-0",S_OK,"2100.0000e-17");
401   VARFMT(VT_R8,V_R8,1.0001e-27,"##00.0000e-0",S_OK,"1000.1000e-30");
402   VARFMT(VT_R8,V_R8,47.11,".0000E+0",S_OK,".4711E+2");
403   VARFMT(VT_R8,V_R8,3.0401e-13,"#####.####e-0%",S_OK,"30401.e-15%");
404   VARFMT(VT_R8,V_R8,1.57,"0.00",S_OK,"1.57");
405   VARFMT(VT_R8,V_R8,-1.57,"0.00",S_OK,"-1.57");
406   VARFMT(VT_R8,V_R8,-1.57,"#.##",S_OK,"-1.57");
407   VARFMT(VT_R8,V_R8,-0.1,".#",S_OK,"-.1");
408   VARFMT(VT_R8,V_R8,0.099,"#.#",S_OK,".1");
409   VARFMT(VT_R8,V_R8,0.0999,"#.##",S_OK,".1");
410   VARFMT(VT_R8,V_R8,0.099,"#.##",S_OK,".1");
411   VARFMT(VT_R8,V_R8,0.0099,"#.##",S_OK,".01");
412   VARFMT(VT_R8,V_R8,0.0049,"#.##",S_OK,".");
413   VARFMT(VT_R8,V_R8,0.0094,"#.##",S_OK,".01");
414   VARFMT(VT_R8,V_R8,0.00099,"#.##",S_OK,".");
415   VARFMT(VT_R8,V_R8,0.0995,"#.##",S_OK,".1");
416   VARFMT(VT_R8,V_R8,8.0995,"#.##",S_OK,"8.1");
417   VARFMT(VT_R8,V_R8,0.0994,"#.##",S_OK,".1");
418   VARFMT(VT_R8,V_R8,1.00,"#,##0.00",S_OK,"1.00");
419   VARFMT(VT_R8,V_R8,0.0995,"#.###",S_OK,".1");
420 
421 
422   /* 'out' is not cleared */
423   out = (BSTR)0x1;
424   hres = pVarFormat(&in,NULL,fd,fw,flags,&out); /* Would crash if out is cleared */
425   ok(hres == S_OK, "got %08x\n", hres);
426   SysFreeString(out);
427   out = NULL;
428 
429   /* VT_NULL */
430   V_VT(&in) = VT_NULL;
431   hres = pVarFormat(&in,NULL,fd,fw,0,&out);
432   ok(hres == S_OK, "VarFormat failed with 0x%08x\n", hres);
433   ok(out == NULL, "expected NULL formatted string\n");
434 
435   /* Invalid args */
436   hres = pVarFormat(&in,NULL,fd,fw,flags,NULL);
437   ok(hres == E_INVALIDARG, "Null out: expected E_INVALIDARG, got 0x%08x\n", hres);
438   hres = pVarFormat(NULL,NULL,fd,fw,flags,&out);
439   ok(hres == E_INVALIDARG, "Null in: expected E_INVALIDARG, got 0x%08x\n", hres);
440   fd = -1;
441   VARFMT(VT_BOOL,V_BOOL,VARIANT_TRUE,"",E_INVALIDARG,"");
442   fd = 8;
443   VARFMT(VT_BOOL,V_BOOL,VARIANT_TRUE,"",E_INVALIDARG,"");
444   fd = 0; fw = -1;
445   VARFMT(VT_BOOL,V_BOOL,VARIANT_TRUE,"",E_INVALIDARG,"");
446   fw = 4;
447   VARFMT(VT_BOOL,V_BOOL,VARIANT_TRUE,"",E_INVALIDARG,"");
448 }
449 
450 static const char *szVarWdnFail =
451     "VarWeekdayName (%d, %d, %d, %d, %x): returned %8x, expected %8x\n";
452 #define VARWDN(iWeekday, fAbbrev, iFirstDay, dwFlags, ret, buff, out, freeOut) \
453 do { \
454   hres = pVarWeekdayName(iWeekday, fAbbrev, iFirstDay, dwFlags, &out); \
455   if (SUCCEEDED(hres)) { \
456     WideCharToMultiByte(CP_ACP, 0, out, -1, buff, sizeof(buff), 0, 0); \
457     if (freeOut) SysFreeString(out); \
458   } else { \
459     buff[0] = '\0'; \
460   } \
461   ok(hres == ret, \
462      szVarWdnFail, \
463      iWeekday, fAbbrev, iFirstDay, dwFlags, &out, hres, ret \
464      ); \
465 } while(0)
466 
467 #define VARWDN_F(iWeekday, fAbbrev, iFirstDay, dwFlags, ret) \
468   VARWDN(iWeekday, fAbbrev, iFirstDay, dwFlags, ret, buff, out, 1)
469 
470 #define VARWDN_O(iWeekday, fAbbrev, iFirstDay, dwFlags) \
471   VARWDN(iWeekday, fAbbrev, iFirstDay, dwFlags, S_OK, buff, out, 0)
472 
test_VarWeekdayName(void)473 static void test_VarWeekdayName(void)
474 {
475   char buff[256];
476   BSTR out = NULL;
477   HRESULT hres;
478   int iWeekday, fAbbrev, iFirstDay;
479   BSTR dayNames[7][2]; /* Monday-Sunday, full/abbr */
480   DWORD defaultFirstDay;
481   int firstDay;
482   int day;
483   int size;
484   DWORD localeValue;
485 
486   CHECKPTR(VarWeekdayName);
487 
488   SetLastError(0xdeadbeef);
489   GetLocaleInfoW(LOCALE_USER_DEFAULT, 0, NULL, 0);
490   if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
491   {
492     win_skip("GetLocaleInfoW is not implemented\n");
493     return;
494   }
495 
496   /* Initialize days' names */
497   for (day = 0; day <= 6; ++day)
498   {
499     for (fAbbrev = 0; fAbbrev <= 1; ++fAbbrev)
500     {
501       localeValue = fAbbrev ? LOCALE_SABBREVDAYNAME1 : LOCALE_SDAYNAME1;
502       localeValue += day;
503       size = GetLocaleInfoW(LOCALE_USER_DEFAULT, localeValue, NULL, 0);
504       dayNames[day][fAbbrev] = SysAllocStringLen(NULL, size - 1);
505       GetLocaleInfoW(LOCALE_USER_DEFAULT, localeValue,
506                      dayNames[day][fAbbrev], size);
507     }
508   }
509 
510   /* Get the user's first day of week. 0=Monday, .. */
511   GetLocaleInfoW(
512       LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK | LOCALE_RETURN_NUMBER,
513       (LPWSTR)&defaultFirstDay, sizeof(defaultFirstDay) / sizeof(WCHAR));
514 
515   /* Check invalid arguments */
516   VARWDN_F(0, 0, 4, 0, E_INVALIDARG);
517   VARWDN_F(8, 0, 4, 0, E_INVALIDARG);
518   VARWDN_F(4, 0, -1, 0, E_INVALIDARG);
519   VARWDN_F(4, 0, 8, 0, E_INVALIDARG);
520 
521   hres = pVarWeekdayName(1, 0, 0, 0, NULL);
522   ok(E_INVALIDARG == hres,
523      "Null pointer: expected E_INVALIDARG, got 0x%08x\n", hres);
524 
525   /* Check all combinations */
526   pVarBstrCmp = (void*)GetProcAddress(hOleaut32, "VarBstrCmp");
527   if (pVarBstrCmp)
528     for (iWeekday = 1; iWeekday <= 7; ++iWeekday)
529     {
530       for (fAbbrev = 0; fAbbrev <= 1; ++fAbbrev)
531       {
532         /* 0 = Default, 1 = Sunday, 2 = Monday, .. */
533         for (iFirstDay = 0; iFirstDay <= 7; ++iFirstDay)
534         {
535           VARWDN_O(iWeekday, fAbbrev, iFirstDay, 0);
536           if (iFirstDay == 0)
537             firstDay = defaultFirstDay;
538           else
539             /* Translate from 0=Sunday to 0=Monday in the modulo 7 space */
540             firstDay = iFirstDay - 2;
541           day = (7 + iWeekday - 1 + firstDay) % 7;
542           ok(VARCMP_EQ == pVarBstrCmp(out, dayNames[day][fAbbrev],
543                                       LOCALE_USER_DEFAULT, 0),
544              "VarWeekdayName(%d,%d,%d): got wrong dayname: '%s'\n",
545              iWeekday, fAbbrev, iFirstDay, buff);
546           SysFreeString(out);
547         }
548       }
549     }
550 
551   /* Cleanup */
552   for (day = 0; day <= 6; ++day)
553   {
554     for (fAbbrev = 0; fAbbrev <= 1; ++fAbbrev)
555     {
556       SysFreeString(dayNames[day][fAbbrev]);
557     }
558   }
559 }
560 
test_VarFormatFromTokens(void)561 static void test_VarFormatFromTokens(void)
562 {
563     static WCHAR number_fmt[] = {'#','#','#',',','#','#','0','.','0','0',0};
564     static const WCHAR number[] = {'6',',','9','0',0};
565     static const WCHAR number_us[] = {'6','9','0','.','0','0',0};
566 
567     static WCHAR date_fmt[] = {'d','d','-','m','m',0};
568     static const WCHAR date[] = {'1','2','-','1','1',0};
569     static const WCHAR date_us[] = {'1','1','-','1','2',0};
570 
571     static WCHAR string_fmt[] = {'@',0};
572     static const WCHAR string_de[] = {'1',',','5',0};
573     static const WCHAR string_us[] = {'1','.','5',0};
574 
575     BYTE buff[256];
576     LCID lcid;
577     VARIANT var;
578     BSTR bstr;
579     HRESULT hres;
580 
581     V_VT(&var) = VT_BSTR;
582     V_BSTR(&var) = SysAllocString(number);
583 
584     lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
585     hres = VarTokenizeFormatString(number_fmt, buff, sizeof(buff), 1, 1, lcid, NULL);
586     ok(hres == S_OK, "VarTokenizeFormatString failed: %x\n", hres);
587     hres = VarFormatFromTokens(&var, number_fmt, buff, 0, &bstr, lcid);
588     ok(hres == S_OK, "VarFormatFromTokens failed: %x\n", hres);
589     ok(!strcmpW(bstr, number_us), "incorrectly formatted number: %s\n", wine_dbgstr_w(bstr));
590     SysFreeString(bstr);
591 
592     lcid = MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT);
593     hres = VarTokenizeFormatString(number_fmt, buff, sizeof(buff), 1, 1, lcid, NULL);
594     ok(hres == S_OK, "VarTokenizeFormatString failed: %x\n", hres);
595     hres = VarFormatFromTokens(&var, number_fmt, buff, 0, &bstr, lcid);
596     ok(hres == S_OK, "VarFormatFromTokens failed: %x\n", hres);
597     ok(!strcmpW(bstr, number), "incorrectly formatted number: %s\n", wine_dbgstr_w(bstr));
598     SysFreeString(bstr);
599 
600     VariantClear(&var);
601 
602     V_VT(&var) = VT_BSTR;
603     V_BSTR(&var) = SysAllocString(date);
604 
605     lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
606     hres = VarTokenizeFormatString(date_fmt, buff, sizeof(buff), 1, 1, lcid, NULL);
607     ok(hres == S_OK, "VarTokenizeFormatString failed: %x\n", hres);
608     hres = VarFormatFromTokens(&var, date_fmt, buff, 0, &bstr, lcid);
609     ok(hres == S_OK, "VarFormatFromTokens failed: %x\n", hres);
610     ok(!strcmpW(bstr, date_us), "incorrectly formatted date: %s\n", wine_dbgstr_w(bstr));
611     SysFreeString(bstr);
612 
613     lcid = MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT);
614     hres = VarTokenizeFormatString(date_fmt, buff, sizeof(buff), 1, 1, lcid, NULL);
615     ok(hres == S_OK, "VarTokenizeFormatString failed: %x\n", hres);
616     hres = VarFormatFromTokens(&var, date_fmt, buff, 0, &bstr, lcid);
617     ok(hres == S_OK, "VarFormatFromTokens failed: %x\n", hres);
618     ok(!strcmpW(bstr, date), "incorrectly formatted date: %s\n", wine_dbgstr_w(bstr));
619     SysFreeString(bstr);
620 
621     VariantClear(&var);
622 
623     V_VT(&var) = VT_R4;
624     V_R4(&var) = 1.5;
625 
626     lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
627     hres = VarTokenizeFormatString(string_fmt, buff, sizeof(buff), 1, 1, lcid, NULL);
628     ok(hres == S_OK, "VarTokenizeFormatString failed: %x\n", hres);
629     hres = VarFormatFromTokens(&var, string_fmt, buff, 0, &bstr, lcid);
630     ok(hres == S_OK, "VarFormatFromTokens failed: %x\n", hres);
631     ok(!strcmpW(bstr, string_us), "incorrectly formatted string: %s\n", wine_dbgstr_w(bstr));
632     SysFreeString(bstr);
633 
634     lcid = MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT);
635     hres = VarTokenizeFormatString(string_fmt, buff, sizeof(buff), 1, 1, lcid, NULL);
636     ok(hres == S_OK, "VarTokenizeFormatString failed: %x\n", hres);
637     hres = VarFormatFromTokens(&var, string_fmt, buff, 0, &bstr, lcid);
638     ok(hres == S_OK, "VarFormatFromTokens failed: %x\n", hres);
639     ok(!strcmpW(bstr, string_de), "incorrectly formatted string: %s\n", wine_dbgstr_w(bstr));
640     SysFreeString(bstr);
641 }
642 
test_GetAltMonthNames(void)643 static void test_GetAltMonthNames(void)
644 {
645     LPOLESTR *str, *str2;
646     HRESULT hr;
647 
648     str = (void *)0xdeadbeef;
649     hr = GetAltMonthNames(0, &str);
650     ok(hr == S_OK, "Unexpected return value %08x\n", hr);
651     ok(str == NULL, "Got %p\n", str);
652 
653     str = (void *)0xdeadbeef;
654     hr = GetAltMonthNames(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), &str);
655     ok(hr == S_OK, "Unexpected return value %08x\n", hr);
656     ok(str == NULL, "Got %p\n", str);
657 
658     str = NULL;
659     hr = GetAltMonthNames(MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_EGYPT), SORT_DEFAULT), &str);
660     ok(hr == S_OK, "Unexpected return value %08x\n", hr);
661     ok(str != NULL, "Got %p\n", str);
662 
663     str2 = NULL;
664     hr = GetAltMonthNames(MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_EGYPT), SORT_DEFAULT), &str2);
665     ok(hr == S_OK, "Unexpected return value %08x\n", hr);
666     ok(str2 == str, "Got %p\n", str2);
667 
668     str = NULL;
669     hr = GetAltMonthNames(MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_DEFAULT), SORT_DEFAULT), &str);
670     ok(hr == S_OK, "Unexpected return value %08x\n", hr);
671     ok(str != NULL, "Got %p\n", str);
672 
673     str = NULL;
674     hr = GetAltMonthNames(MAKELCID(MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT), SORT_DEFAULT), &str);
675     ok(hr == S_OK, "Unexpected return value %08x\n", hr);
676     ok(str != NULL, "Got %p\n", str);
677 }
678 
START_TEST(varformat)679 START_TEST(varformat)
680 {
681   hOleaut32 = GetModuleHandleA("oleaut32.dll");
682 
683   has_i8 = GetProcAddress(hOleaut32, "VarI8FromI1") != NULL;
684 
685   test_VarFormatNumber();
686   test_VarFormat();
687   test_VarWeekdayName();
688   test_VarFormatFromTokens();
689   test_GetAltMonthNames();
690 }
691