1 /* Unit test suite for datetime control.
2 *
3 * Copyright 2007 Kanit Therdsteerasukdi
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19 
20 #include <windows.h>
21 #include <commctrl.h>
22 
23 #include "wine/test.h"
24 #include "msg.h"
25 #include "v6util.h"
26 
27 #define expect(EXPECTED, GOT) ok((GOT)==(EXPECTED), "Expected %d, got %ld\n", (EXPECTED), (GOT))
28 
29 #define expect_unsuccess(EXPECTED, GOT) ok((GOT)==(EXPECTED), "Expected %d(unsuccessful), got %ld(successful)\n", (EXPECTED), (GOT))
30 
31 #define NUM_MSG_SEQUENCES   1
32 #define DATETIME_SEQ_INDEX    0
33 
34 static BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
35 
36 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
37 
38 static const struct message test_dtm_set_format_seq[] = {
39     { DTM_SETFORMATA, sent|wparam|lparam, 0, 0 },
40     { DTM_SETFORMATA, sent|wparam, 0 },
41     { 0 }
42 };
43 
44 static const struct message test_dtm_set_and_get_mccolor_seq[] = {
45     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_BACKGROUND, 0 },
46     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_BACKGROUND, RGB(255, 255, 255) },
47     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_BACKGROUND, RGB(100, 180, 220) },
48     { DTM_GETMCCOLOR, sent|wparam|lparam, MCSC_BACKGROUND, 0 },
49     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_MONTHBK, 0 },
50     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_MONTHBK, RGB(255, 255, 255) },
51     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_MONTHBK, RGB(100, 180, 220) },
52     { DTM_GETMCCOLOR, sent|wparam|lparam, MCSC_MONTHBK, 0 },
53     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TEXT, 0 },
54     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TEXT, RGB(255, 255, 255) },
55     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TEXT, RGB(100, 180, 220) },
56     { DTM_GETMCCOLOR, sent|wparam|lparam, MCSC_TEXT, 0 },
57     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TITLEBK, 0 },
58     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TITLEBK, RGB(255, 255, 255) },
59     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TITLEBK, RGB(100, 180, 220) },
60     { DTM_GETMCCOLOR, sent|wparam|lparam, MCSC_TITLEBK, 0 },
61     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TITLETEXT, 0 },
62     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TITLETEXT, RGB(255, 255, 255) },
63     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TITLETEXT, RGB(100, 180, 220) },
64     { DTM_GETMCCOLOR, sent|wparam|lparam, MCSC_TITLETEXT, 0 },
65     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TRAILINGTEXT, 0 },
66     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TRAILINGTEXT, RGB(255, 255, 255) },
67     { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TRAILINGTEXT, RGB(100, 180, 220) },
68     { DTM_GETMCCOLOR, sent|wparam|lparam, MCSC_TRAILINGTEXT, 0 },
69     { 0 }
70 };
71 
72 static const struct message test_dtm_set_and_get_mcfont_seq[] = {
73     { DTM_SETMCFONT, sent|lparam, 0, 1 },
74     { DTM_GETMCFONT, sent|wparam|lparam, 0, 0 },
75     { 0 }
76 };
77 
78 static const struct message test_dtm_get_monthcal_seq[] = {
79     { DTM_GETMONTHCAL, sent|wparam|lparam, 0, 0 },
80     { 0 }
81 };
82 
83 static const struct message test_dtm_set_and_get_range_seq[] = {
84     { DTM_SETRANGE, sent|wparam, GDTR_MIN },
85     { DTM_GETRANGE, sent|wparam, 0 },
86     { DTM_SETRANGE, sent|wparam, GDTR_MAX },
87     { DTM_SETRANGE, sent|wparam, GDTR_MAX },
88     { DTM_GETRANGE, sent|wparam, 0 },
89     { DTM_SETRANGE, sent|wparam, GDTR_MIN },
90     { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
91     { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
92     { DTM_GETRANGE, sent|wparam, 0 },
93     { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
94     { DTM_GETRANGE, sent|wparam, 0 },
95     { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
96     { DTM_GETRANGE, sent|wparam, 0 },
97     { 0 }
98 };
99 
100 static const struct message test_dtm_set_range_swap_min_max_seq[] = {
101     { DTM_SETSYSTEMTIME, sent|wparam, 0 },
102     { DTM_GETSYSTEMTIME, sent|wparam, 0 },
103     { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
104     { DTM_GETRANGE, sent|wparam, 0 },
105     { DTM_SETSYSTEMTIME, sent|wparam, 0 },
106     { DTM_GETSYSTEMTIME, sent|wparam, 0 },
107     { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
108     { DTM_GETRANGE, sent|wparam, 0 },
109     { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
110     { DTM_GETRANGE, sent|wparam, 0 },
111     { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
112     { DTM_GETRANGE, sent|wparam, 0 },
113     { 0 }
114 };
115 
116 static const struct message test_dtm_set_and_get_system_time_seq[] = {
117     { DTM_SETSYSTEMTIME, sent|wparam, GDT_NONE },
118     { DTM_GETSYSTEMTIME, sent|wparam, 0 },
119     { DTM_SETSYSTEMTIME, sent|wparam, 0 },
120     { DTM_SETSYSTEMTIME, sent|wparam, 0 },
121     { DTM_SETSYSTEMTIME, sent|wparam, 0 },
122     { DTM_GETSYSTEMTIME, sent|wparam, 0 },
123     { DTM_SETSYSTEMTIME, sent|wparam, 0 },
124     { 0 }
125 };
126 
127 static const struct message test_dtm_set_and_get_systime_with_limits[] = {
128     { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
129     { DTM_GETRANGE, sent|wparam, 0 },
130     { DTM_SETSYSTEMTIME, sent|wparam, 0 },
131     { DTM_GETSYSTEMTIME, sent|wparam, 0 },
132     { DTM_SETSYSTEMTIME, sent|wparam, 0 },
133     { DTM_GETSYSTEMTIME, sent|wparam, 0 },
134     { DTM_SETSYSTEMTIME, sent|wparam, 0 },
135     { DTM_GETSYSTEMTIME, sent|wparam, 0 },
136     { 0 }
137 };
138 
139 static LRESULT WINAPI datetime_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
140 {
141     WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
142     static LONG defwndproc_counter = 0;
143     struct message msg = { 0 };
144     LRESULT ret;
145 
146     msg.message = message;
147     msg.flags = sent|wparam|lparam;
148     if (defwndproc_counter) msg.flags |= defwinproc;
149     msg.wParam = wParam;
150     msg.lParam = lParam;
151     add_message(sequences, DATETIME_SEQ_INDEX, &msg);
152 
153     defwndproc_counter++;
154     ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
155     defwndproc_counter--;
156 
157     return ret;
158 }
159 
160 static HWND create_datetime_control(DWORD style)
161 {
162     WNDPROC oldproc;
163     HWND hWndDateTime = NULL;
164 
165     hWndDateTime = CreateWindowExA(0,
166         DATETIMEPICK_CLASSA,
167         NULL,
168         style,
169         0,50,300,120,
170         NULL,
171         NULL,
172         NULL,
173         NULL);
174 
175     if (!hWndDateTime) return NULL;
176 
177     oldproc = (WNDPROC)SetWindowLongPtrA(hWndDateTime, GWLP_WNDPROC,
178                                          (LONG_PTR)datetime_subclass_proc);
179     SetWindowLongPtrA(hWndDateTime, GWLP_USERDATA, (LONG_PTR)oldproc);
180 
181     return hWndDateTime;
182 }
183 
184 static void test_dtm_set_format(void)
185 {
186     HWND hWnd;
187     CHAR txt[256];
188     SYSTEMTIME systime;
189     LRESULT r;
190 
191     hWnd = create_datetime_control(DTS_SHOWNONE);
192 
193     flush_sequences(sequences, NUM_MSG_SEQUENCES);
194 
195     r = SendMessageA(hWnd, DTM_SETFORMATA, 0, 0);
196     expect(1, r);
197 
198     r = SendMessageA(hWnd, DTM_SETFORMATA, 0,
199 		    (LPARAM)"'Today is: 'hh':'m':'s dddd MMM dd', 'yyyy");
200     expect(1, r);
201 
202     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_format_seq, "test_dtm_set_format", FALSE);
203 
204     r = SendMessageA(hWnd, DTM_SETFORMATA, 0, (LPARAM)"'hh' hh");
205     expect(1, r);
206     ZeroMemory(&systime, sizeof(systime));
207     systime.wYear = 2000;
208     systime.wMonth = systime.wDay = 1;
209     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, 0, (LPARAM)&systime);
210     expect(1, r);
211     GetWindowTextA(hWnd, txt, 256);
212     ok(strcmp(txt, "hh 12") == 0, "String mismatch (\"%s\" vs \"hh 12\")\n", txt);
213 
214     DestroyWindow(hWnd);
215 }
216 
217 static void test_mccolor_types(HWND hWndDateTime, int mccolor_type, const char* mccolor_name)
218 {
219     COLORREF theColor, prevColor, crColor;
220 
221     theColor=RGB(0,0,0);
222     crColor = SendMessageA(hWndDateTime, DTM_SETMCCOLOR, mccolor_type, theColor);
223     ok(crColor != ~0u, "%s: Set RGB(0,0,0): Expected COLORREF of previous value, got %d\n", mccolor_name, crColor);
224     prevColor=theColor;
225     theColor=RGB(255,255,255);
226     crColor = SendMessageA(hWndDateTime, DTM_SETMCCOLOR, mccolor_type, theColor);
227     ok(crColor==prevColor, "%s: Set RGB(255,255,255): Expected COLORREF of previous value, got %d\n", mccolor_name, crColor);
228     prevColor=theColor;
229     theColor=RGB(100,180,220);
230     crColor = SendMessageA(hWndDateTime, DTM_SETMCCOLOR, mccolor_type, theColor);
231     ok(crColor==prevColor, "%s: Set RGB(100,180,220): Expected COLORREF of previous value, got %d\n", mccolor_name, crColor);
232     crColor = SendMessageA(hWndDateTime, DTM_GETMCCOLOR, mccolor_type, 0);
233     ok(crColor==theColor, "%s: GETMCCOLOR: Expected %d, got %d\n", mccolor_name, theColor, crColor);
234 }
235 
236 static void test_dtm_set_and_get_mccolor(void)
237 {
238     HWND hWnd;
239 
240     hWnd = create_datetime_control(DTS_SHOWNONE);
241 
242     flush_sequences(sequences, NUM_MSG_SEQUENCES);
243 
244     test_mccolor_types(hWnd, MCSC_BACKGROUND, "MCSC_BACKGROUND");
245     test_mccolor_types(hWnd, MCSC_MONTHBK, "MCSC_MONTHBK");
246     test_mccolor_types(hWnd, MCSC_TEXT, "MCSC_TEXT");
247     test_mccolor_types(hWnd, MCSC_TITLEBK, "MCSC_TITLEBK");
248     test_mccolor_types(hWnd, MCSC_TITLETEXT, "MCSC_TITLETEXT");
249     test_mccolor_types(hWnd, MCSC_TRAILINGTEXT, "MCSC_TRAILINGTEXT");
250 
251     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_and_get_mccolor_seq, "test_dtm_set_and_get_mccolor", FALSE);
252 
253     DestroyWindow(hWnd);
254 }
255 
256 static void test_dtm_set_and_get_mcfont(void)
257 {
258     HFONT hFontOrig, hFontNew;
259     HWND hWnd;
260 
261     hWnd = create_datetime_control(DTS_SHOWNONE);
262 
263     flush_sequences(sequences, NUM_MSG_SEQUENCES);
264 
265     hFontOrig = GetStockObject(DEFAULT_GUI_FONT);
266     SendMessageA(hWnd, DTM_SETMCFONT, (WPARAM)hFontOrig, TRUE);
267     hFontNew = (HFONT)SendMessageA(hWnd, DTM_GETMCFONT, 0, 0);
268     ok(hFontOrig == hFontNew, "Expected hFontOrig==hFontNew, hFontOrig=%p, hFontNew=%p\n", hFontOrig, hFontNew);
269 
270     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_and_get_mcfont_seq, "test_dtm_set_and_get_mcfont", FALSE);
271     DestroyWindow(hWnd);
272 }
273 
274 static void test_dtm_get_monthcal(void)
275 {
276     LRESULT r;
277     HWND hWnd;
278 
279     hWnd = create_datetime_control(DTS_SHOWNONE);
280 
281     flush_sequences(sequences, NUM_MSG_SEQUENCES);
282 
283     todo_wine {
284         r = SendMessageA(hWnd, DTM_GETMONTHCAL, 0, 0);
285         ok(r == 0, "Expected NULL(no child month calendar control), got %ld\n", r);
286     }
287 
288     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_get_monthcal_seq, "test_dtm_get_monthcal", FALSE);
289     DestroyWindow(hWnd);
290 }
291 
292 static void fill_systime_struct(SYSTEMTIME *st, int year, int month, int dayofweek, int day, int hour, int minute, int second, int milliseconds)
293 {
294     st->wYear = year;
295     st->wMonth = month;
296     st->wDayOfWeek = dayofweek;
297     st->wDay = day;
298     st->wHour = hour;
299     st->wMinute = minute;
300     st->wSecond = second;
301     st->wMilliseconds = milliseconds;
302 }
303 
304 static LPARAM compare_systime_date(SYSTEMTIME *st1, SYSTEMTIME *st2)
305 {
306     return (st1->wYear == st2->wYear)
307             && (st1->wMonth == st2->wMonth)
308             && (st1->wDayOfWeek == st2->wDayOfWeek)
309             && (st1->wDay == st2->wDay);
310 }
311 
312 static LPARAM compare_systime_time(SYSTEMTIME *st1, SYSTEMTIME *st2)
313 {
314     return (st1->wHour == st2->wHour)
315             && (st1->wMinute == st2->wMinute)
316             && (st1->wSecond == st2->wSecond)
317             && (st1->wMilliseconds == st2->wMilliseconds);
318 }
319 
320 static LPARAM compare_systime(SYSTEMTIME *st1, SYSTEMTIME *st2)
321 {
322     if(!compare_systime_date(st1, st2))
323         return 0;
324 
325     return compare_systime_time(st1, st2);
326 }
327 
328 #define expect_systime(ST1, ST2) ok(compare_systime((ST1), (ST2))==1, "ST1 != ST2\n")
329 #define expect_systime_date(ST1, ST2) ok(compare_systime_date((ST1), (ST2))==1, "ST1.date != ST2.date\n")
330 #define expect_systime_time(ST1, ST2) ok(compare_systime_time((ST1), (ST2))==1, "ST1.time != ST2.time\n")
331 
332 static void test_dtm_set_and_get_range(void)
333 {
334     LRESULT r;
335     SYSTEMTIME st[2];
336     SYSTEMTIME getSt[2];
337     HWND hWnd;
338 
339     hWnd = create_datetime_control(DTS_SHOWNONE);
340 
341     flush_sequences(sequences, NUM_MSG_SEQUENCES);
342 
343     /* initialize st[0] to lowest possible value */
344     fill_systime_struct(&st[0], 1601, 1, 0, 1, 0, 0, 0, 0);
345     /* initialize st[1] to all invalid numbers */
346     fill_systime_struct(&st[1], 0, 0, 7, 0, 24, 60, 60, 1000);
347 
348     r = SendMessageA(hWnd, DTM_SETRANGE, GDTR_MIN, (LPARAM)st);
349     expect(1, r);
350     r = SendMessageA(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
351     ok(r == GDTR_MIN, "Expected %x, not %x(GDTR_MAX) or %x(GDTR_MIN | GDTR_MAX), got %lx\n", GDTR_MIN, GDTR_MAX, GDTR_MIN | GDTR_MAX, r);
352     expect_systime(&st[0], &getSt[0]);
353 
354     r = SendMessageA(hWnd, DTM_SETRANGE, GDTR_MAX, (LPARAM)st);
355     expect_unsuccess(0, r);
356 
357     /* set st[0] to all invalid numbers */
358     fill_systime_struct(&st[0], 0, 0, 7, 0, 24, 60, 60, 1000);
359     /* set st[1] to highest possible value */
360     fill_systime_struct(&st[1], 30827, 12, 6, 31, 23, 59, 59, 999);
361 
362     r = SendMessageA(hWnd, DTM_SETRANGE, GDTR_MAX, (LPARAM)st);
363     expect(1, r);
364     r = SendMessageA(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
365     ok(r == GDTR_MAX, "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MIN | GDTR_MAX), got %lx\n", GDTR_MAX, GDTR_MIN, GDTR_MIN | GDTR_MAX, r);
366     expect_systime(&st[1], &getSt[1]);
367 
368     r = SendMessageA(hWnd, DTM_SETRANGE, GDTR_MIN, (LPARAM)st);
369     expect_unsuccess(0, r);
370     r = SendMessageA(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
371     expect_unsuccess(0, r);
372 
373     /* set st[0] to highest possible value */
374     fill_systime_struct(&st[0], 30827, 12, 6, 31, 23, 59, 59, 999);
375 
376     r = SendMessageA(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
377     expect(1, r);
378     r = SendMessageA(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
379     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
380     expect_systime(&st[0], &getSt[0]);
381     expect_systime(&st[1], &getSt[1]);
382 
383     /* initialize st[0] to lowest possible value */
384     fill_systime_struct(&st[0], 1601, 1, 0, 1, 0, 0, 0, 0);
385     /* set st[1] to highest possible value */
386     fill_systime_struct(&st[1], 30827, 12, 6, 31, 23, 59, 59, 999);
387 
388     r = SendMessageA(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
389     expect(1, r);
390     r = SendMessageA(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
391     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
392     expect_systime(&st[0], &getSt[0]);
393     expect_systime(&st[1], &getSt[1]);
394 
395     /* set st[0] to value higher than minimum */
396     fill_systime_struct(&st[0], 1980, 1, 3, 23, 14, 34, 37, 465);
397     /* set st[1] to value lower than maximum */
398     fill_systime_struct(&st[1], 2007, 3, 2, 31, 23, 59, 59, 999);
399 
400     r = SendMessageA(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
401     expect(1, r);
402     r = SendMessageA(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
403     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
404     expect_systime(&st[0], &getSt[0]);
405     expect_systime(&st[1], &getSt[1]);
406 
407     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_and_get_range_seq, "test_dtm_set_and_get_range", FALSE);
408 
409     /* DTM_SETRANGE with 0 flags */
410     r = SendMessageA(hWnd, DTM_SETRANGE, 0, (LPARAM)st);
411     ok(r, "got %lu\n", r);
412     r = SendMessageA(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
413     ok(r == 0, "got %lu\n", r);
414     ok(getSt[0].wYear == 0 && getSt[1].wYear == 0, "got %u, %u\n", getSt[0].wYear, getSt[1].wYear);
415 
416     DestroyWindow(hWnd);
417 }
418 
419 /* when max<min for DTM_SETRANGE, Windows seems to swap the min and max values,
420 although that's undocumented.  However, it doesn't seem to be implemented
421 correctly, causing some strange side effects */
422 static void test_dtm_set_range_swap_min_max(void)
423 {
424     LRESULT r;
425     SYSTEMTIME st[2];
426     SYSTEMTIME getSt[2];
427     SYSTEMTIME origSt;
428     HWND hWnd;
429 
430     hWnd = create_datetime_control(DTS_SHOWNONE);
431     flush_sequences(sequences, NUM_MSG_SEQUENCES);
432 
433     fill_systime_struct(&st[0], 2007, 2, 4, 15, 2, 2, 2, 2);
434 
435     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st[0]);
436     expect(1, r);
437     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&origSt);
438     ok(r == GDT_VALID, "Expected %d, not %d(GDT_NONE) or %d(GDT_ERROR), got %ld\n", GDT_VALID, GDT_NONE, GDT_ERROR, r);
439     expect_systime(&st[0], &origSt);
440 
441     /* set st[0] to value higher than st[1] */
442     fill_systime_struct(&st[0], 2007, 3, 2, 31, 23, 59, 59, 999);
443     fill_systime_struct(&st[1], 1980, 1, 3, 23, 14, 34, 37, 465);
444 
445     /* since min>max, min and max values should be swapped by DTM_SETRANGE
446     automatically */
447     r = SendMessageA(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
448     expect(1, r);
449     r = SendMessageA(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
450     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
451     todo_wine {
452         ok(compare_systime(&st[0], &getSt[0]) == 1 ||
453            broken(compare_systime(&st[0], &getSt[1]) == 1), /* comctl32 version  <= 5.80 */
454            "ST1 != ST2\n");
455 
456         ok(compare_systime(&st[1], &getSt[1]) == 1 ||
457            broken(compare_systime(&st[1], &getSt[0]) == 1), /* comctl32 version  <= 5.80 */
458            "ST1 != ST2\n");
459     }
460 
461     fill_systime_struct(&st[0], 1980, 1, 3, 23, 14, 34, 37, 465);
462 
463     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st[0]);
464     expect(1, r);
465     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt[0]);
466     ok(r == GDT_VALID, "Expected %d, not %d(GDT_NONE) or %d(GDT_ERROR), got %ld\n", GDT_VALID, GDT_NONE, GDT_ERROR, r);
467     /* the time part seems to not change after swapping the min and max values
468     and doing DTM_SETSYSTEMTIME */
469     expect_systime_date(&st[0], &getSt[0]);
470     todo_wine {
471         ok(compare_systime_time(&origSt, &getSt[0]) == 1 ||
472            broken(compare_systime_time(&st[0], &getSt[0]) == 1), /* comctl32 version  <= 5.80 */
473            "ST1.time != ST2.time\n");
474     }
475 
476     /* set st[0] to value higher than minimum */
477     fill_systime_struct(&st[0], 1980, 1, 3, 23, 14, 34, 37, 465);
478     /* set st[1] to value lower than maximum */
479     fill_systime_struct(&st[1], 2007, 3, 2, 31, 23, 59, 59, 999);
480 
481     r = SendMessageA(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
482     expect(1, r);
483     /* for some reason after we swapped the min and max values before,
484     whenever we do a DTM_SETRANGE, the DTM_GETRANGE will return the values
485     swapped*/
486     r = SendMessageA(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
487     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
488     todo_wine {
489         ok(compare_systime(&st[0], &getSt[1]) == 1 ||
490            broken(compare_systime(&st[0], &getSt[0]) == 1), /* comctl32 version  <= 5.80 */
491            "ST1 != ST2\n");
492 
493         ok(compare_systime(&st[1], &getSt[0]) == 1 ||
494            broken(compare_systime(&st[1], &getSt[1]) == 1), /* comctl32 version <= 5.80 */
495            "ST1 != ST2\n");
496     }
497 
498     /* set st[0] to value higher than st[1] */
499     fill_systime_struct(&st[0], 2007, 3, 2, 31, 23, 59, 59, 999);
500     fill_systime_struct(&st[1], 1980, 1, 3, 23, 14, 34, 37, 465);
501 
502     /* set min>max again, so that the return values of DTM_GETRANGE are no
503     longer swapped the next time we do a DTM SETRANGE and DTM_GETRANGE*/
504     r = SendMessageA(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
505     expect(1, r);
506     r = SendMessageA(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
507     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
508     expect_systime(&st[0], &getSt[1]);
509     expect_systime(&st[1], &getSt[0]);
510 
511     /* initialize st[0] to lowest possible value */
512     fill_systime_struct(&st[0], 1601, 1, 0, 1, 0, 0, 0, 0);
513     /* set st[1] to highest possible value */
514     fill_systime_struct(&st[1], 30827, 12, 6, 31, 23, 59, 59, 999);
515 
516     r = SendMessageA(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
517     expect(1, r);
518     r = SendMessageA(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
519     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
520     expect_systime(&st[0], &getSt[0]);
521     expect_systime(&st[1], &getSt[1]);
522 
523     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_range_swap_min_max_seq, "test_dtm_set_range_swap_min_max", FALSE);
524 
525     DestroyWindow(hWnd);
526 }
527 
528 static void test_dtm_set_and_get_system_time(void)
529 {
530     LRESULT r;
531     SYSTEMTIME st, getSt, ref;
532     HWND hWnd;
533 
534     hWnd = create_datetime_control(0);
535     ok(hWnd !=NULL, "Expected non NULL, got %p\n", hWnd);
536     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_NONE, (LPARAM)&st);
537     expect(0, r);
538 
539     DestroyWindow(hWnd);
540 
541     hWnd = create_datetime_control(DTS_SHOWNONE);
542     flush_sequences(sequences, NUM_MSG_SEQUENCES);
543 
544     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_NONE, (LPARAM)&st);
545     expect(1, r);
546     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
547     ok(r == GDT_NONE, "Expected %d, not %d(GDT_VALID) or %d(GDT_ERROR), got %ld\n", GDT_NONE, GDT_VALID, GDT_ERROR, r);
548 
549     /* set st to lowest possible value */
550     fill_systime_struct(&st, 1601, 1, 0, 1, 0, 0, 0, 0);
551 
552     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
553     expect(1, r);
554 
555     /* set st to highest possible value */
556     fill_systime_struct(&st, 30827, 12, 6, 31, 23, 59, 59, 999);
557 
558     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
559     expect(1, r);
560 
561     /* set st to value between min and max */
562     fill_systime_struct(&st, 1980, 1, 3, 23, 14, 34, 37, 465);
563 
564     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
565     expect(1, r);
566     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
567     ok(r == GDT_VALID, "Expected %d, not %d(GDT_NONE) or %d(GDT_ERROR), got %ld\n", GDT_VALID, GDT_NONE, GDT_ERROR, r);
568     expect_systime(&st, &getSt);
569 
570     /* set st to invalid value */
571     fill_systime_struct(&st, 0, 0, 7, 0, 24, 60, 60, 1000);
572 
573     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
574     expect_unsuccess(0, r);
575 
576     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_and_get_system_time_seq, "test_dtm_set_and_get_system_time", FALSE);
577 
578     /* set to some valid value */
579     GetSystemTime(&ref);
580     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&ref);
581     expect(1, r);
582     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
583     expect(GDT_VALID, r);
584     expect_systime(&ref, &getSt);
585 
586     /* year invalid */
587     st = ref;
588     st.wYear = 0;
589     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
590     todo_wine expect(1, r);
591     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
592     expect(GDT_VALID, r);
593     expect_systime(&ref, &getSt);
594     /* month invalid */
595     st = ref;
596     st.wMonth = 13;
597     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
598     expect(0, r);
599     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
600     expect(GDT_VALID, r);
601     expect_systime(&ref, &getSt);
602     /* day invalid */
603     st = ref;
604     st.wDay = 32;
605     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
606     expect(0, r);
607     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
608     expect(GDT_VALID, r);
609     expect_systime(&ref, &getSt);
610     /* day invalid for current month */
611     st = ref;
612     st.wDay = 30;
613     st.wMonth = 2;
614     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
615     expect(0, r);
616     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
617     expect(GDT_VALID, r);
618     expect_systime(&ref, &getSt);
619     /* day of week isn't validated */
620     st = ref;
621     st.wDayOfWeek = 10;
622     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
623     expect(1, r);
624     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
625     expect(GDT_VALID, r);
626     expect_systime(&ref, &getSt);
627     /* hour invalid */
628     st = ref;
629     st.wHour = 25;
630     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
631     expect(0, r);
632     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
633     expect(GDT_VALID, r);
634     expect_systime(&ref, &getSt);
635     /* minute invalid */
636     st = ref;
637     st.wMinute = 60;
638     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
639     expect(0, r);
640     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
641     expect(GDT_VALID, r);
642     expect_systime(&ref, &getSt);
643     /* sec invalid */
644     st = ref;
645     st.wSecond = 60;
646     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
647     expect(0, r);
648     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
649     expect(GDT_VALID, r);
650     expect_systime(&ref, &getSt);
651     /* msec invalid */
652     st = ref;
653     st.wMilliseconds = 1000;
654     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
655     expect(0, r);
656     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
657     expect(GDT_VALID, r);
658     expect_systime(&ref, &getSt);
659 
660     /* day of week should be calculated automatically,
661        actual day of week for this date is 4 */
662     fill_systime_struct(&st, 2009, 10, 1, 1, 0, 0, 10, 200);
663     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
664     expect(1, r);
665     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
666     expect(GDT_VALID, r);
667     /* 01.10.2009 is Thursday */
668     expect(4, (LRESULT)getSt.wDayOfWeek);
669     st.wDayOfWeek = 4;
670     expect_systime(&st, &getSt);
671 }
672 
673 static void test_dtm_set_and_get_systemtime_with_limits(void)
674 {
675     LRESULT r;
676     SYSTEMTIME st[2], getSt[2], refSt;
677     HWND hWnd;
678 
679     hWnd = create_datetime_control(DTS_SHOWNONE);
680 
681     flush_sequences(sequences, NUM_MSG_SEQUENCES);
682 
683     /* set range */
684     fill_systime_struct(&st[0], 1980, 1, 3, 23, 14, 34, 37, 465);
685     fill_systime_struct(&st[1], 2007, 3, 2, 31, 23, 59, 59, 999);
686 
687     r = SendMessageA(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
688     expect(1, r);
689     r = SendMessageA(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
690     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
691     expect_systime(&st[0], &getSt[0]);
692     expect_systime(&st[1], &getSt[1]);
693 
694     /* Initially set a valid time */
695     fill_systime_struct(&refSt, 1999, 9, 4, 9, 19, 9, 9, 999);
696     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&refSt);
697     expect(1, r);
698     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt[0]);
699     ok(r == GDT_VALID, "Expected %d, not %d(GDT_NONE) or %d(GDT_ERROR), got %ld\n", GDT_VALID, GDT_NONE, GDT_ERROR, r);
700     expect_systime(&refSt, &getSt[0]);
701 
702     /* Now set an out-of-bounds time */
703     fill_systime_struct(&st[0], 2010, 1, 0, 1, 0, 0, 0, 0);
704 
705     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st[0]);
706     expect(1, r);
707     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt[0]);
708     ok(r == GDT_VALID, "Expected %d, not %d(GDT_NONE) or %d(GDT_ERROR), got %ld\n", GDT_VALID, GDT_NONE, GDT_ERROR, r);
709     expect_systime(&refSt, &getSt[0]);
710 
711     fill_systime_struct(&st[0], 1977, 1, 0, 1, 0, 0, 0, 0);
712 
713     r = SendMessageA(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st[0]);
714     expect(1, r);
715     r = SendMessageA(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt[0]);
716     ok(r == GDT_VALID, "Expected %d, not %d(GDT_NONE) or %d(GDT_ERROR), got %ld\n", GDT_VALID, GDT_NONE, GDT_ERROR, r);
717     expect_systime(&refSt, &getSt[0]);
718 
719     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_and_get_systime_with_limits, "test_dtm_set_and_get_systime_with_limits", FALSE);
720 
721     DestroyWindow(hWnd);
722 }
723 
724 static void test_dtm_get_ideal_size(void)
725 {
726     HWND hwnd;
727     HDC hdc;
728     HFONT hfont;
729     LOGFONTA lf;
730     TEXTMETRICA tm;
731     SIZE size;
732     BOOL r;
733 
734     hwnd = create_datetime_control(0);
735     r = SendMessageA(hwnd, DTM_GETIDEALSIZE, 0, (LPARAM)&size);
736     if (!r)
737     {
738         win_skip("DTM_GETIDEALSIZE is not available\n");
739         DestroyWindow(hwnd);
740         return;
741     }
742 
743     /* Set font so that the test is consistent on Wine and Windows */
744     ZeroMemory(&lf, sizeof(lf));
745     lf.lfWeight = FW_NORMAL;
746     lf.lfHeight = 20;
747     lstrcpyA(lf.lfFaceName, "Tahoma");
748     hfont = CreateFontIndirectA(&lf);
749     SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
750 
751     hdc = GetDC(hwnd);
752     GetTextMetricsA(hdc, &tm);
753     ReleaseDC(hwnd, hdc);
754 
755     r = SendMessageA(hwnd, DTM_GETIDEALSIZE, 0, (LPARAM)&size);
756     ok(r, "Expect DTM_GETIDEALSIZE message to return true\n");
757     ok(size.cx > 0 && size.cy >= tm.tmHeight,
758        "Expect size.cx > 0 and size.cy >= %d, got cx:%d cy:%d\n", tm.tmHeight, size.cx, size.cy);
759 
760     DestroyWindow(hwnd);
761     DeleteObject(hfont);
762 }
763 
764 static void test_wm_set_get_text(void)
765 {
766     static const CHAR a_str[] = "a";
767     CHAR buff[16], time[16], caltype[3];
768     HWND hWnd;
769     LRESULT ret;
770 
771     hWnd = create_datetime_control(0);
772 
773     ret = SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)a_str);
774     ok(CB_ERR == ret ||
775        broken(0 == ret) || /* comctl32 <= 4.72 */
776        broken(1 == ret), /* comctl32 <= 4.70 */
777        "Expected CB_ERR, got %ld\n", ret);
778 
779     buff[0] = 0;
780     ret = SendMessageA(hWnd, WM_GETTEXT, sizeof(buff), (LPARAM)buff);
781     ok(strcmp(buff, a_str) != 0, "Expected text to change, got %s\n", buff);
782     ok(ret != 0, "Expected non-zero return value\n");
783 
784     SetLastError(0xdeadbeef);
785     ret = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ICALENDARTYPE, caltype, 3);
786     if (ret == 0)
787         skip("Must know local calendar type (%x)\n", GetLastError());
788     else if (atoi(caltype) != CAL_GREGORIAN)
789         skip("DateTimePicker Control only supports Gregorian calendar (type: %s)\n", caltype);
790     else {
791         SetLastError(0xdeadbeef);
792         ret = GetDateFormatA(LOCALE_USER_DEFAULT, 0, NULL, NULL, time, sizeof(time));
793         if (ret == 0)
794             skip("GetDateFormat failed, returned %ld, error %d\n", ret, GetLastError());
795         else
796             ok(!strcmp(buff, time), "Expected %s, got %s\n", time, buff);
797     }
798 
799     DestroyWindow(hWnd);
800 }
801 
802 static void test_dts_shownone(void)
803 {
804     HWND hwnd;
805     DWORD style;
806 
807     /* it isn't allowed to change DTS_SHOWNONE after creation */
808     hwnd = create_datetime_control(0);
809     style = GetWindowLongA(hwnd, GWL_STYLE);
810     SetWindowLongA(hwnd, GWL_STYLE, style | DTS_SHOWNONE);
811     style = GetWindowLongA(hwnd, GWL_STYLE);
812     ok(!(style & DTS_SHOWNONE), "Expected DTS_SHOWNONE not to be set\n");
813     DestroyWindow(hwnd);
814 
815     hwnd = create_datetime_control(DTS_SHOWNONE);
816     style = GetWindowLongA(hwnd, GWL_STYLE);
817     SetWindowLongA(hwnd, GWL_STYLE, style & ~DTS_SHOWNONE);
818     style = GetWindowLongA(hwnd, GWL_STYLE);
819     ok(style & DTS_SHOWNONE, "Expected DTS_SHOWNONE to be set\n");
820     DestroyWindow(hwnd);
821 }
822 
823 static void init_functions(void)
824 {
825     HMODULE hComCtl32 = LoadLibraryA("comctl32.dll");
826 
827 #define X(f) p##f = (void*)GetProcAddress(hComCtl32, #f);
828     X(InitCommonControlsEx);
829 #undef X
830 }
831 
832 START_TEST(datetime)
833 {
834     INITCOMMONCONTROLSEX iccex;
835     ULONG_PTR cookie;
836     HANDLE ctxt;
837 
838     init_functions();
839 
840     iccex.dwSize = sizeof(iccex);
841     iccex.dwICC  = ICC_DATE_CLASSES;
842     pInitCommonControlsEx(&iccex);
843 
844     init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
845 
846     test_dtm_set_format();
847     test_dtm_set_and_get_mccolor();
848     test_dtm_set_and_get_mcfont();
849     test_dtm_get_monthcal();
850     test_dtm_set_and_get_range();
851     test_dtm_set_range_swap_min_max();
852     test_dtm_set_and_get_system_time();
853     test_dtm_set_and_get_systemtime_with_limits();
854     test_wm_set_get_text();
855     test_dts_shownone();
856 
857     if (!load_v6_module(&cookie, &ctxt))
858         return;
859 
860     test_dtm_set_format();
861     test_dtm_set_and_get_mccolor();
862     test_dtm_set_and_get_mcfont();
863     test_dtm_get_monthcal();
864     test_dtm_get_ideal_size();
865     test_wm_set_get_text();
866     test_dts_shownone();
867 
868     unload_v6_module(cookie, ctxt);
869 }
870