1 /* Unit tests for the up-down control
2  *
3  * Copyright 2005 C. Scott Ananian
4  * Copyright (C) 2007 James Hawkins
5  * Copyright (C) 2007 Leslie Choong
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 /* TO TEST:
23  *   - send click messages to the up-down control, check the current position
24  *   - up-down control automatically positions itself next to its buddy window
25  *   - up-down control sets the caption of the buddy window
26  *   - test CreateUpDownControl API
27  *   - check UDS_AUTOBUDDY style, up-down control selects previous window in z-order
28  *   - check UDM_SETBUDDY message
29  *   - check UDM_GETBUDDY message
30  *   - up-down control and buddy control must have the same parent
31  *   - up-down control notifies its parent window when its position changes with UDN_DELTAPOS + WM_VSCROLL or WM_HSCROLL
32  *   - check UDS_ALIGN[LEFT,RIGHT]...check that width of buddy window is decreased
33  *   - check that UDS_SETBUDDYINT sets the caption of the buddy window when it is changed
34  *   - check that the thousands operator is set for large numbers
35  *   - check that the thousands operator is not set with UDS_NOTHOUSANDS
36  *   - check UDS_ARROWKEYS, control subclasses the buddy window so that it processes the keys when it has focus
37  *   - check UDS_HORZ
38  *   - check changing past min/max values
39  *   - check UDS_WRAP wraps values past min/max, incrementing past upper value wraps position to lower value
40  *   - can change control's position, min/max pos, radix
41  *   - check UDM_GETPOS, for up-down control with a buddy window, position is the caption of the buddy window, so change the
42  *     caption of the buddy window then call UDM_GETPOS
43  *   - check UDM_SETRANGE, max can be less than min, so clicking the up arrow decreases the current position
44  *   - more stuff to test
45  */
46 
47 #include <windows.h>
48 #include <commctrl.h>
49 #include <stdio.h>
50 
51 #include "wine/test.h"
52 #include "msg.h"
53 
54 #define expect(EXPECTED,GOT) ok((GOT)==(EXPECTED), "Expected %d, got %d\n", (EXPECTED), (GOT))
55 
56 #define NUM_MSG_SEQUENCES   3
57 #define PARENT_SEQ_INDEX    0
58 #define EDIT_SEQ_INDEX      1
59 #define UPDOWN_SEQ_INDEX    2
60 
61 #define UPDOWN_ID           0
62 #define BUDDY_ID            1
63 
64 static HWND parent_wnd, g_edit;
65 
66 static HWND (WINAPI *pCreateUpDownControl)(DWORD, INT, INT, INT, INT,
67     HWND, INT, HINSTANCE, HWND, INT, INT, INT);
68 static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
69 
70 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
71 
72 static const struct message add_updown_with_edit_seq[] = {
73     { WM_WINDOWPOSCHANGING, sent },
74     { WM_NCCALCSIZE, sent|wparam, TRUE },
75     { WM_WINDOWPOSCHANGED, sent },
76     { WM_SIZE, sent|wparam|defwinproc, SIZE_RESTORED /*, MAKELONG(91, 75) exact size depends on font */ },
77     { 0 }
78 };
79 
80 static const struct message add_updown_to_parent_seq[] = {
81     { WM_NOTIFYFORMAT, sent|lparam, 0, NF_QUERY },
82     { WM_QUERYUISTATE, sent|optional },
83     { WM_PARENTNOTIFY, sent|wparam, MAKELONG(WM_CREATE, WM_CREATE) },
84     { 0 }
85 };
86 
87 static const struct message get_edit_text_seq[] = {
88     { WM_GETTEXT, sent },
89     { 0 }
90 };
91 
92 static const struct message test_updown_pos_seq[] = {
93     { UDM_SETRANGE, sent|lparam, 0, MAKELONG(100,0) },
94     { UDM_GETRANGE, sent},
95     { UDM_SETPOS, sent|lparam, 0, 5},
96     { UDM_GETPOS, sent},
97     { UDM_SETPOS, sent|lparam, 0, 0},
98     { UDM_GETPOS, sent},
99     { UDM_SETPOS, sent|lparam, 0, MAKELONG(-1,0)},
100     { UDM_GETPOS, sent},
101     { UDM_SETPOS, sent|lparam, 0, 100},
102     { UDM_GETPOS, sent},
103     { UDM_SETPOS, sent|lparam, 0, 101},
104     { UDM_GETPOS, sent},
105     { 0 }
106 };
107 
108 static const struct message test_updown_pos32_seq[] = {
109     { UDM_SETRANGE32, sent|lparam, 0, 1000 },
110     { UDM_GETRANGE32, sent}, /* Cannot check wparam and lparam as they are ptrs */
111     { UDM_SETPOS32, sent|lparam, 0, 500 },
112     { UDM_GETPOS32, sent},
113     { UDM_SETPOS32, sent|lparam, 0, 0 },
114     { UDM_GETPOS32, sent},
115     { UDM_SETPOS32, sent|lparam, 0, -1 },
116     { UDM_GETPOS32, sent},
117     { UDM_SETPOS32, sent|lparam, 0, 1000 },
118     { UDM_GETPOS32, sent},
119     { UDM_SETPOS32, sent|lparam, 0, 1001 },
120     { UDM_GETPOS32, sent},
121     { 0 }
122 };
123 
124 static const struct message test_updown_buddy_seq[] = {
125     { UDM_GETBUDDY, sent },
126     { UDM_SETBUDDY, sent },
127     { WM_STYLECHANGING, sent|defwinproc },
128     { WM_STYLECHANGED, sent|defwinproc },
129     { WM_STYLECHANGING, sent|defwinproc },
130     { WM_STYLECHANGED, sent|defwinproc },
131     { WM_WINDOWPOSCHANGING, sent|defwinproc },
132     { WM_NCCALCSIZE, sent|wparam|optional|defwinproc, 1 },
133     { WM_WINDOWPOSCHANGED, sent|defwinproc },
134     { WM_MOVE, sent|defwinproc },
135     { UDM_GETBUDDY, sent },
136     { 0 }
137 };
138 
139 static const struct message test_updown_base_seq[] = {
140     { UDM_SETBASE, sent|wparam, 10 },
141     { UDM_GETBASE, sent },
142     { UDM_SETBASE, sent|wparam, 80 },
143     { UDM_GETBASE, sent },
144     { UDM_SETBASE, sent|wparam, 16 },
145     { UDM_GETBASE, sent },
146     { UDM_SETBASE, sent|wparam, 80 },
147     { UDM_GETBASE, sent },
148     { UDM_SETBASE, sent|wparam, 10 },
149     { UDM_GETBASE, sent },
150     { 0 }
151 };
152 
153 static const struct message test_updown_unicode_seq[] = {
154     { UDM_SETUNICODEFORMAT, sent|wparam, 0 },
155     { UDM_GETUNICODEFORMAT, sent },
156     { UDM_SETUNICODEFORMAT, sent|wparam, 1 },
157     { UDM_GETUNICODEFORMAT, sent },
158     { UDM_SETUNICODEFORMAT, sent|wparam, 0 },
159     { UDM_GETUNICODEFORMAT, sent },
160     { 0 }
161 };
162 
163 static const struct message test_updown_pos_nochange_seq[] = {
164     { WM_GETTEXT, sent|id, 0, 0, BUDDY_ID },
165     { 0 }
166 };
167 
168 static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
169 {
170     static LONG defwndproc_counter = 0;
171     struct message msg = { 0 };
172     LRESULT ret;
173 
174     /* log system messages, except for painting */
175     if (message < WM_USER &&
176         message != WM_PAINT &&
177         message != WM_ERASEBKGND &&
178         message != WM_NCPAINT &&
179         message != WM_NCHITTEST &&
180         message != WM_GETTEXT &&
181         message != WM_GETICON &&
182         message != WM_DEVICECHANGE)
183     {
184         msg.message = message;
185         msg.flags = sent|wparam|lparam;
186         if (defwndproc_counter) msg.flags |= defwinproc;
187         msg.wParam = wParam;
188         msg.lParam = lParam;
189         add_message(sequences, PARENT_SEQ_INDEX, &msg);
190     }
191 
192     defwndproc_counter++;
193     ret = DefWindowProcA(hwnd, message, wParam, lParam);
194     defwndproc_counter--;
195 
196     return ret;
197 }
198 
199 static BOOL register_parent_wnd_class(void)
200 {
201     WNDCLASSA cls;
202 
203     cls.style = 0;
204     cls.lpfnWndProc = parent_wnd_proc;
205     cls.cbClsExtra = 0;
206     cls.cbWndExtra = 0;
207     cls.hInstance = GetModuleHandleA(NULL);
208     cls.hIcon = 0;
209     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
210     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
211     cls.lpszMenuName = NULL;
212     cls.lpszClassName = "Up-Down test parent class";
213     return RegisterClassA(&cls);
214 }
215 
216 static HWND create_parent_window(void)
217 {
218     if (!register_parent_wnd_class())
219         return NULL;
220 
221     return CreateWindowExA(0, "Up-Down test parent class",
222                           "Up-Down test parent window",
223                           WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
224                           WS_MAXIMIZEBOX | WS_VISIBLE,
225                           0, 0, 100, 100,
226                           GetDesktopWindow(), NULL, GetModuleHandleA(NULL), NULL);
227 }
228 
229 static LRESULT WINAPI edit_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
230 {
231     WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
232     static LONG defwndproc_counter = 0;
233     struct message msg = { 0 };
234     LRESULT ret;
235 
236     msg.message = message;
237     msg.flags = sent|wparam|lparam;
238     if (defwndproc_counter) msg.flags |= defwinproc;
239     msg.wParam = wParam;
240     msg.lParam = lParam;
241     msg.id     = BUDDY_ID;
242     add_message(sequences, EDIT_SEQ_INDEX, &msg);
243 
244     defwndproc_counter++;
245     ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
246     defwndproc_counter--;
247     return ret;
248 }
249 
250 static HWND create_edit_control(void)
251 {
252     WNDPROC oldproc;
253     HWND hwnd;
254     RECT rect;
255 
256     GetClientRect(parent_wnd, &rect);
257     hwnd = CreateWindowExA(0, WC_EDITA, NULL, WS_CHILD | WS_BORDER | WS_VISIBLE,
258                            0, 0, rect.right, rect.bottom,
259                            parent_wnd, NULL, GetModuleHandleA(NULL), NULL);
260     if (!hwnd) return NULL;
261 
262     oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
263                                          (LONG_PTR)edit_subclass_proc);
264     SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc);
265 
266     return hwnd;
267 }
268 
269 static LRESULT WINAPI updown_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
270 {
271     WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
272     static LONG defwndproc_counter = 0;
273     struct message msg = { 0 };
274     LRESULT ret;
275 
276     msg.message = message;
277     msg.flags = sent|wparam|lparam;
278     if (defwndproc_counter) msg.flags |= defwinproc;
279     msg.wParam = wParam;
280     msg.lParam = lParam;
281     msg.id     = UPDOWN_ID;
282     add_message(sequences, UPDOWN_SEQ_INDEX, &msg);
283 
284     defwndproc_counter++;
285     ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
286     defwndproc_counter--;
287 
288     return ret;
289 }
290 
291 static HWND create_updown_control(DWORD style, HWND buddy)
292 {
293     WNDPROC oldproc;
294     HWND updown;
295     RECT rect;
296 
297     GetClientRect(parent_wnd, &rect);
298     updown = CreateWindowExA(0, UPDOWN_CLASSA, NULL, WS_CHILD | WS_BORDER | WS_VISIBLE | style,
299                            0, 0, rect.right, rect.bottom,
300                            parent_wnd, (HMENU)1, GetModuleHandleA(NULL), NULL);
301     ok(updown != NULL, "Failed to create UpDown control.\n");
302     if (!updown) return NULL;
303 
304     SendMessageA(updown, UDM_SETBUDDY, (WPARAM)buddy, 0);
305     SendMessageA(updown, UDM_SETRANGE, 0, MAKELONG(100, 0));
306     SendMessageA(updown, UDM_SETPOS, 0, MAKELONG(50, 0));
307 
308     oldproc = (WNDPROC)SetWindowLongPtrA(updown, GWLP_WNDPROC,
309                                          (LONG_PTR)updown_subclass_proc);
310     SetWindowLongPtrA(updown, GWLP_USERDATA, (LONG_PTR)oldproc);
311 
312     return updown;
313 }
314 
315 static void test_updown_pos(void)
316 {
317     HWND updown;
318     int r;
319 
320     updown = create_updown_control(UDS_ALIGNRIGHT, g_edit);
321 
322     flush_sequences(sequences, NUM_MSG_SEQUENCES);
323 
324     /* Set Range from 0 to 100 */
325     SendMessageA(updown, UDM_SETRANGE, 0 , MAKELONG(100,0) );
326     r = SendMessageA(updown, UDM_GETRANGE, 0,0);
327     expect(100,LOWORD(r));
328     expect(0,HIWORD(r));
329 
330     /* Set the position to 5, return is not checked as it was set before func call */
331     SendMessageA(updown, UDM_SETPOS, 0 , MAKELONG(5,0) );
332     /* Since UDM_SETBUDDYINT was not set at creation HIWORD(r) will always be 1 as a return from UDM_GETPOS */
333     /* Get the position, which should be 5 */
334     r = SendMessageA(updown, UDM_GETPOS, 0 , 0 );
335     expect(5,LOWORD(r));
336     expect(1,HIWORD(r));
337 
338     /* Set the position to 0, return should be 5 */
339     r = SendMessageA(updown, UDM_SETPOS, 0 , MAKELONG(0,0) );
340     expect(5,r);
341     /* Get the position, which should be 0 */
342     r = SendMessageA(updown, UDM_GETPOS, 0 , 0 );
343     expect(0,LOWORD(r));
344     expect(1,HIWORD(r));
345 
346     /* Set the position to -1, return should be 0 */
347     r = SendMessageA(updown, UDM_SETPOS, 0 , MAKELONG(-1,0) );
348     expect(0,r);
349     /* Get the position, which should be 0 */
350     r = SendMessageA(updown, UDM_GETPOS, 0 , 0 );
351     expect(0,LOWORD(r));
352     expect(1,HIWORD(r));
353 
354     /* Set the position to 100, return should be 0 */
355     r = SendMessageA(updown, UDM_SETPOS, 0 , MAKELONG(100,0) );
356     expect(0,r);
357     /* Get the position, which should be 100 */
358     r = SendMessageA(updown, UDM_GETPOS, 0 , 0 );
359     expect(100,LOWORD(r));
360     expect(1,HIWORD(r));
361 
362     /* Set the position to 101, return should be 100 */
363     r = SendMessageA(updown, UDM_SETPOS, 0 , MAKELONG(101,0) );
364     expect(100,r);
365     /* Get the position, which should be 100 */
366     r = SendMessageA(updown, UDM_GETPOS, 0 , 0 );
367     expect(100,LOWORD(r));
368     expect(1,HIWORD(r));
369 
370     ok_sequence(sequences, UPDOWN_SEQ_INDEX, test_updown_pos_seq , "test updown pos", FALSE);
371 
372     DestroyWindow(updown);
373 
374     /* there's no attempt to update buddy Edit if text didn't change */
375     SetWindowTextA(g_edit, "50");
376     updown = create_updown_control(UDS_ALIGNRIGHT | UDS_SETBUDDYINT | UDS_ARROWKEYS, g_edit);
377 
378     /* test sequence only on 5.8x versions */
379     r = SendMessageA(updown, UDM_GETPOS32, 0, 0);
380     if (r)
381     {
382         UDACCEL accel;
383 
384         flush_sequences(sequences, NUM_MSG_SEQUENCES);
385 
386         r = SendMessageA(updown, UDM_SETPOS, 0, 50);
387         expect(50,r);
388 
389         ok_sequence(sequences, EDIT_SEQ_INDEX, test_updown_pos_nochange_seq,
390                     "test updown pos, no change", FALSE);
391 
392         SendMessageA(updown, UDM_SETRANGE, 0, MAKELONG(1, 40));
393         r = SendMessageA(updown, UDM_GETRANGE, 0, 0);
394         expect(1, LOWORD(r));
395         expect(40, HIWORD(r));
396 
397         accel.nSec = 0;
398         accel.nInc = 5;
399         r = SendMessageA(updown, UDM_SETACCEL, 1, (LPARAM)&accel);
400         expect(TRUE, r);
401 
402         r = SendMessageA(updown, UDM_GETPOS, 0, 0);
403         expect(40, LOWORD(r));
404         expect(1, HIWORD(r));
405 
406         r = SendMessageA(updown, UDM_SETPOS, 0, MAKELONG(0, 0));
407         expect(40, LOWORD(r));
408         expect(0, HIWORD(r));
409 
410         r = SendMessageA(updown, UDM_GETPOS, 0, 0);
411         expect(1, LOWORD(r));
412         expect(0, HIWORD(r));
413 
414         r = SendMessageA(updown, UDM_SETPOS, 0, MAKELONG(2, 0));
415         expect(1, LOWORD(r));
416         expect(0, HIWORD(r));
417 
418         r = SendMessageA(g_edit, WM_KEYDOWN, VK_UP, 0);
419         expect(0, r);
420         r = SendMessageA(updown, UDM_GETPOS, 0, 0);
421         expect(1, LOWORD(r));
422         expect(0, HIWORD(r));
423 
424         r = SendMessageA(updown, UDM_SETPOS, 0, MAKELONG(50, 0));
425         expect(1, LOWORD(r));
426         expect(0, HIWORD(r));
427 
428         r = SendMessageA(updown, UDM_GETPOS, 0, 0);
429         expect(40, LOWORD(r));
430         expect(0, HIWORD(r));
431     }
432 
433     DestroyWindow(updown);
434 }
435 
436 static void test_updown_pos32(void)
437 {
438     HWND updown;
439     int r;
440     int low, high;
441 
442     updown = create_updown_control(UDS_ALIGNRIGHT, g_edit);
443 
444     flush_sequences(sequences, NUM_MSG_SEQUENCES);
445 
446     /* Set the position to 0 to 1000 */
447     SendMessageA(updown, UDM_SETRANGE32, 0 , 1000 );
448 
449     low = high = -1;
450     r = SendMessageA(updown, UDM_GETRANGE32, (WPARAM) &low , (LPARAM) &high );
451     expect(0,r);
452     if (low == -1)
453     {
454         win_skip("UDM_SETRANGE32/UDM_GETRANGE32 not available\n");
455         DestroyWindow(updown);
456         return;
457     }
458 
459     expect(0,low);
460     expect(1000,high);
461 
462     /* Set position to 500 */
463     r = SendMessageA(updown, UDM_SETPOS32, 0 , 500 );
464     if (!r)
465     {
466         win_skip("UDM_SETPOS32 and UDM_GETPOS32 need 5.80\n");
467         DestroyWindow(updown);
468         return;
469     }
470     expect(50,r);
471 
472     /* Since UDM_SETBUDDYINT was not set at creation bRet will always be true as a return from UDM_GETPOS32 */
473 
474     r = SendMessageA(updown, UDM_GETPOS32, 0 , (LPARAM) &high );
475     expect(500,r);
476     expect(1,high);
477 
478     /* Set position to 0, return should be 500 */
479     r = SendMessageA(updown, UDM_SETPOS32, 0 , 0 );
480     expect(500,r);
481     r = SendMessageA(updown, UDM_GETPOS32, 0 , (LPARAM) &high );
482     expect(0,r);
483     expect(1,high);
484 
485     /* Set position to -1 which should become 0, return should be 0 */
486     r = SendMessageA(updown, UDM_SETPOS32, 0 , -1 );
487     expect(0,r);
488     r = SendMessageA(updown, UDM_GETPOS32, 0 , (LPARAM) &high );
489     expect(0,r);
490     expect(1,high);
491 
492     /* Set position to 1000, return should be 0 */
493     r = SendMessageA(updown, UDM_SETPOS32, 0 , 1000 );
494     expect(0,r);
495     r = SendMessageA(updown, UDM_GETPOS32, 0 , (LPARAM) &high );
496     expect(1000,r);
497     expect(1,high);
498 
499     /* Set position to 1001 which should become 1000, return should be 1000 */
500     r = SendMessageA(updown, UDM_SETPOS32, 0 , 1001 );
501     expect(1000,r);
502     r = SendMessageA(updown, UDM_GETPOS32, 0 , (LPARAM) &high );
503     expect(1000,r);
504     expect(1,high);
505 
506     ok_sequence(sequences, UPDOWN_SEQ_INDEX, test_updown_pos32_seq, "test updown pos32", FALSE);
507 
508     DestroyWindow(updown);
509 
510     /* there's no attempt to update buddy Edit if text didn't change */
511     SetWindowTextA(g_edit, "50");
512     updown = create_updown_control(UDS_ALIGNRIGHT | UDS_SETBUDDYINT, g_edit);
513 
514     flush_sequences(sequences, NUM_MSG_SEQUENCES);
515 
516     r = SendMessageA(updown, UDM_SETPOS32, 0, 50);
517     expect(50,r);
518     ok_sequence(sequences, EDIT_SEQ_INDEX, test_updown_pos_nochange_seq,
519                 "test updown pos, no change", FALSE);
520 
521     DestroyWindow(updown);
522 }
523 
524 static void test_updown_buddy(void)
525 {
526     HWND updown, buddyReturn, buddy;
527     RECT rect, rect2;
528     WNDPROC proc;
529     DWORD style;
530 
531     updown = create_updown_control(UDS_ALIGNRIGHT, g_edit);
532 
533     flush_sequences(sequences, NUM_MSG_SEQUENCES);
534 
535     buddyReturn = (HWND)SendMessageA(updown, UDM_GETBUDDY, 0 , 0 );
536     ok(buddyReturn == g_edit, "Expected edit handle\n");
537 
538     buddyReturn = (HWND)SendMessageA(updown, UDM_SETBUDDY, (WPARAM) g_edit, 0);
539     ok(buddyReturn == g_edit, "Expected edit handle\n");
540 
541     buddyReturn = (HWND)SendMessageA(updown, UDM_GETBUDDY, 0 , 0 );
542     ok(buddyReturn == g_edit, "Expected edit handle\n");
543 
544     ok_sequence(sequences, UPDOWN_SEQ_INDEX, test_updown_buddy_seq, "test updown buddy", TRUE);
545     ok_sequence(sequences, EDIT_SEQ_INDEX, add_updown_with_edit_seq, "test updown buddy_edit", FALSE);
546 
547     DestroyWindow(updown);
548 
549     buddy = create_edit_control();
550     proc  = (WNDPROC)GetWindowLongPtrA(buddy, GWLP_WNDPROC);
551 
552     updown= create_updown_control(UDS_ALIGNRIGHT, buddy);
553     ok(proc == (WNDPROC)GetWindowLongPtrA(buddy, GWLP_WNDPROC), "No subclassing expected\n");
554 
555     style = GetWindowLongA(updown, GWL_STYLE);
556     SetWindowLongA(updown, GWL_STYLE, style | UDS_ARROWKEYS);
557     style = GetWindowLongA(updown, GWL_STYLE);
558     ok(style & UDS_ARROWKEYS, "Expected UDS_ARROWKEYS\n");
559     /* no subclass if UDS_ARROWKEYS set after creation */
560     ok(proc == (WNDPROC)GetWindowLongPtrA(buddy, GWLP_WNDPROC), "No subclassing expected\n");
561 
562     DestroyWindow(updown);
563 
564     updown= create_updown_control(UDS_ALIGNRIGHT | UDS_ARROWKEYS, buddy);
565     ok(proc != (WNDPROC)GetWindowLongPtrA(buddy, GWLP_WNDPROC), "Subclassing expected\n");
566 
567     if (pSetWindowSubclass)
568     {
569         /* updown uses subclass helpers for buddy on >5.8x systems */
570         ok(GetPropA(buddy, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n");
571     }
572 
573     DestroyWindow(updown);
574     DestroyWindow(buddy);
575 
576     /* Create with buddy and UDS_HORZ, reset buddy. */
577     updown = create_updown_control(UDS_HORZ, g_edit);
578 
579     buddyReturn = (HWND)SendMessageA(updown, UDM_GETBUDDY, 0, 0);
580     ok(buddyReturn == g_edit, "Unexpected buddy window.\n");
581 
582     GetClientRect(updown, &rect);
583 
584     buddyReturn = (HWND)SendMessageA(updown, UDM_SETBUDDY, 0, 0);
585     ok(buddyReturn == g_edit, "Unexpected buddy window.\n");
586 
587     GetClientRect(updown, &rect2);
588     ok(EqualRect(&rect, &rect2), "Unexpected window rect.\n");
589 
590     /* Remove UDS_HORZ, reset buddy again. */
591     style = GetWindowLongA(updown, GWL_STYLE);
592     SetWindowLongA(updown, GWL_STYLE, style & ~UDS_HORZ);
593     style = GetWindowLongA(updown, GWL_STYLE);
594     ok(!(style & UDS_HORZ), "Unexpected style.\n");
595 
596     buddyReturn = (HWND)SendMessageA(updown, UDM_SETBUDDY, 0, 0);
597     ok(buddyReturn == NULL, "Unexpected buddy window.\n");
598 
599     GetClientRect(updown, &rect2);
600     ok(EqualRect(&rect, &rect2), "Unexpected window rect.\n");
601 
602     DestroyWindow(updown);
603 
604     /* Without UDS_HORZ. */
605     updown = create_updown_control(0, g_edit);
606 
607     buddyReturn = (HWND)SendMessageA(updown, UDM_GETBUDDY, 0, 0);
608     ok(buddyReturn == g_edit, "Unexpected buddy window.\n");
609 
610     GetClientRect(updown, &rect);
611 
612     buddyReturn = (HWND)SendMessageA(updown, UDM_SETBUDDY, 0, 0);
613     ok(buddyReturn == g_edit, "Unexpected buddy window.\n");
614 
615     GetClientRect(updown, &rect2);
616     ok(EqualRect(&rect, &rect2), "Unexpected window rect.\n");
617 
618     DestroyWindow(updown);
619 
620     /* Create without buddy. */
621     GetClientRect(parent_wnd, &rect);
622     updown = CreateWindowExA(0, UPDOWN_CLASSA, NULL, WS_CHILD | WS_BORDER | WS_VISIBLE | UDS_HORZ,
623         0, 0, rect.right, rect.bottom, parent_wnd, (HMENU)1, GetModuleHandleA(NULL), NULL);
624     ok(updown != NULL, "Failed to create UpDown control.\n");
625 
626     GetClientRect(updown, &rect);
627     buddyReturn = (HWND)SendMessageA(updown, UDM_SETBUDDY, 0, 0);
628     ok(buddyReturn == NULL, "Unexpected buddy window.\n");
629     GetClientRect(updown, &rect2);
630 
631     ok(EqualRect(&rect, &rect2), "Unexpected window rect.\n");
632 
633     style = GetWindowLongA(updown, GWL_STYLE);
634     SetWindowLongA(updown, GWL_STYLE, style & ~UDS_HORZ);
635 
636     GetClientRect(updown, &rect2);
637     ok(EqualRect(&rect, &rect2), "Unexpected window rect.\n");
638 
639     buddyReturn = (HWND)SendMessageA(updown, UDM_SETBUDDY, (WPARAM)g_edit, 0);
640     ok(buddyReturn == NULL, "Unexpected buddy window.\n");
641     GetClientRect(updown, &rect);
642 
643     buddyReturn = (HWND)SendMessageA(updown, UDM_SETBUDDY, 0, 0);
644     ok(buddyReturn == g_edit, "Unexpected buddy window.\n");
645     GetClientRect(updown, &rect2);
646 todo_wine
647     ok(EqualRect(&rect, &rect2), "Unexpected window rect.\n");
648 
649     DestroyWindow(updown);
650 }
651 
652 static void test_updown_base(void)
653 {
654     HWND updown;
655     int r;
656     CHAR text[10];
657 
658     updown = create_updown_control(UDS_ALIGNRIGHT, g_edit);
659 
660     flush_sequences(sequences, NUM_MSG_SEQUENCES);
661 
662     SendMessageA(updown, UDM_SETBASE, 10 , 0);
663     r = SendMessageA(updown, UDM_GETBASE, 0 , 0);
664     expect(10,r);
665 
666     /* Set base to an invalid value, should return 0 and stay at 10 */
667     r = SendMessageA(updown, UDM_SETBASE, 80 , 0);
668     expect(0,r);
669     r = SendMessageA(updown, UDM_GETBASE, 0 , 0);
670     expect(10,r);
671 
672     /* Set base to 16 now, should get 16 as the return */
673     r = SendMessageA(updown, UDM_SETBASE, 16 , 0);
674     expect(10,r);
675     r = SendMessageA(updown, UDM_GETBASE, 0 , 0);
676     expect(16,r);
677 
678     /* Set base to an invalid value, should return 0 and stay at 16 */
679     r = SendMessageA(updown, UDM_SETBASE, 80 , 0);
680     expect(0,r);
681     r = SendMessageA(updown, UDM_GETBASE, 0 , 0);
682     expect(16,r);
683 
684     /* Set base back to 10, return should be 16 */
685     r = SendMessageA(updown, UDM_SETBASE, 10 , 0);
686     expect(16,r);
687     r = SendMessageA(updown, UDM_GETBASE, 0 , 0);
688     expect(10,r);
689 
690     ok_sequence(sequences, UPDOWN_SEQ_INDEX, test_updown_base_seq, "test updown base", FALSE);
691 
692     DestroyWindow(updown);
693 
694     /* switch base with buddy attached */
695     updown = create_updown_control(UDS_SETBUDDYINT | UDS_ALIGNRIGHT, g_edit);
696 
697     r = SendMessageA(updown, UDM_SETPOS, 0, 10);
698     expect(50, r);
699 
700     GetWindowTextA(g_edit, text, sizeof(text)/sizeof(CHAR));
701     ok(lstrcmpA(text, "10") == 0, "Expected '10', got '%s'\n", text);
702 
703     r = SendMessageA(updown, UDM_SETBASE, 16, 0);
704     expect(10, r);
705 
706     GetWindowTextA(g_edit, text, sizeof(text)/sizeof(CHAR));
707     /* FIXME: currently hex output isn't properly formatted, but for this
708        test only change from initial text matters */
709     ok(lstrcmpA(text, "10") != 0, "Expected '0x000A', got '%s'\n", text);
710 
711     DestroyWindow(updown);
712 }
713 
714 static void test_updown_unicode(void)
715 {
716     HWND updown;
717     int r;
718 
719     updown = create_updown_control(UDS_ALIGNRIGHT, g_edit);
720 
721     flush_sequences(sequences, NUM_MSG_SEQUENCES);
722 
723     /* Set it to ANSI, don't check return as we don't know previous state */
724     SendMessageA(updown, UDM_SETUNICODEFORMAT, 0 , 0);
725     r = SendMessageA(updown, UDM_GETUNICODEFORMAT, 0 , 0);
726     expect(0,r);
727 
728     /* Now set it to Unicode format */
729     r = SendMessageA(updown, UDM_SETUNICODEFORMAT, 1 , 0);
730     expect(0,r);
731     r = SendMessageA(updown, UDM_GETUNICODEFORMAT, 0 , 0);
732     if (!r)
733     {
734         win_skip("UDM_SETUNICODEFORMAT not available\n");
735         DestroyWindow(updown);
736         return;
737     }
738     expect(1,r);
739 
740     /* And now set it back to ANSI */
741     r = SendMessageA(updown, UDM_SETUNICODEFORMAT, 0 , 0);
742     expect(1,r);
743     r = SendMessageA(updown, UDM_GETUNICODEFORMAT, 0 , 0);
744     expect(0,r);
745 
746     ok_sequence(sequences, UPDOWN_SEQ_INDEX, test_updown_unicode_seq, "test updown unicode", FALSE);
747 
748     DestroyWindow(updown);
749 }
750 
751 static void test_updown_create(void)
752 {
753     CHAR text[MAX_PATH];
754     HWND updown;
755     RECT r;
756 
757     flush_sequences(sequences, NUM_MSG_SEQUENCES);
758 
759     updown = create_updown_control(UDS_ALIGNRIGHT, g_edit);
760     ok(updown != NULL, "Failed to create updown control\n");
761     ok_sequence(sequences, PARENT_SEQ_INDEX, add_updown_to_parent_seq, "add updown control to parent", TRUE);
762     ok_sequence(sequences, EDIT_SEQ_INDEX, add_updown_with_edit_seq, "add updown control with edit", FALSE);
763 
764     flush_sequences(sequences, NUM_MSG_SEQUENCES);
765 
766     GetWindowTextA(g_edit, text, MAX_PATH);
767     ok(lstrlenA(text) == 0, "Expected empty string\n");
768     ok_sequence(sequences, EDIT_SEQ_INDEX, get_edit_text_seq, "get edit text", FALSE);
769 
770     DestroyWindow(updown);
771 
772     /* create with zero width */
773     updown = CreateWindowA (UPDOWN_CLASSA, 0, WS_CHILD | WS_BORDER | WS_VISIBLE, 0, 0, 0, 0,
774                    parent_wnd, (HMENU)(DWORD_PTR)1, GetModuleHandleA(NULL), 0);
775     ok(updown != NULL, "Failed to create updown control\n");
776     r.right = 0;
777     GetClientRect(updown, &r);
778     ok(r.right > 0, "Expected default width, got %d\n", r.right);
779     DestroyWindow(updown);
780     /* create with really small width */
781     updown = CreateWindowA (UPDOWN_CLASSA, 0, WS_CHILD | WS_BORDER | WS_VISIBLE, 0, 0, 2, 0,
782                    parent_wnd, (HMENU)(DWORD_PTR)1, GetModuleHandleA(NULL), 0);
783     ok(updown != NULL, "Failed to create updown control\n");
784     r.right = 0;
785     GetClientRect(updown, &r);
786     ok(r.right != 2 && r.right > 0, "Expected default width, got %d\n", r.right);
787     DestroyWindow(updown);
788     /* create with width greater than default */
789     updown = CreateWindowA (UPDOWN_CLASSA, 0, WS_CHILD | WS_BORDER | WS_VISIBLE, 0, 0, 100, 0,
790                    parent_wnd, (HMENU)(DWORD_PTR)1, GetModuleHandleA(NULL), 0);
791     ok(updown != NULL, "Failed to create updown control\n");
792     r.right = 0;
793     GetClientRect(updown, &r);
794     ok(r.right < 100 && r.right > 0, "Expected default width, got %d\n", r.right);
795     DestroyWindow(updown);
796     /* create with zero height, UDS_HORZ */
797     updown = CreateWindowA (UPDOWN_CLASSA, 0, UDS_HORZ | WS_CHILD | WS_BORDER | WS_VISIBLE, 0, 0, 0, 0,
798                    parent_wnd, (HMENU)(DWORD_PTR)1, GetModuleHandleA(NULL), 0);
799     ok(updown != NULL, "Failed to create updown control\n");
800     r.bottom = 0;
801     GetClientRect(updown, &r);
802     ok(r.bottom == 0, "Expected zero height, got %d\n", r.bottom);
803     DestroyWindow(updown);
804     /* create with really small height, UDS_HORZ */
805     updown = CreateWindowA (UPDOWN_CLASSA, 0, UDS_HORZ | WS_CHILD | WS_BORDER | WS_VISIBLE, 0, 0, 0, 2,
806                    parent_wnd, (HMENU)(DWORD_PTR)1, GetModuleHandleA(NULL), 0);
807     ok(updown != NULL, "Failed to create updown control\n");
808     r.bottom = 0;
809     GetClientRect(updown, &r);
810     ok(r.bottom == 0, "Expected zero height, got %d\n", r.bottom);
811     DestroyWindow(updown);
812     /* create with height greater than default, UDS_HORZ */
813     updown = CreateWindowA (UPDOWN_CLASSA, 0, UDS_HORZ | WS_CHILD | WS_BORDER | WS_VISIBLE, 0, 0, 0, 100,
814                    parent_wnd, (HMENU)(DWORD_PTR)1, GetModuleHandleA(NULL), 0);
815     ok(updown != NULL, "Failed to create updown control\n");
816     r.bottom = 0;
817     GetClientRect(updown, &r);
818     ok(r.bottom < 100 && r.bottom > 0, "Expected default height, got %d\n", r.bottom);
819     DestroyWindow(updown);
820 }
821 
822 static void test_UDS_SETBUDDYINT(void)
823 {
824     HWND updown;
825     DWORD style, ret;
826     CHAR text[10];
827 
828     /* cleanup buddy */
829     text[0] = '\0';
830     SetWindowTextA(g_edit, text);
831 
832     /* creating without UDS_SETBUDDYINT */
833     updown = create_updown_control(UDS_ALIGNRIGHT, g_edit);
834     /* try to set UDS_SETBUDDYINT after creation */
835     style = GetWindowLongA(updown, GWL_STYLE);
836     SetWindowLongA(updown, GWL_STYLE, style | UDS_SETBUDDYINT);
837     style = GetWindowLongA(updown, GWL_STYLE);
838     ok(style & UDS_SETBUDDYINT, "Expected UDS_SETBUDDY to be set\n");
839     SendMessageA(updown, UDM_SETPOS, 0, 20);
840     GetWindowTextA(g_edit, text, sizeof(text)/sizeof(CHAR));
841     ok(lstrlenA(text) == 0, "Expected empty string\n");
842     DestroyWindow(updown);
843 
844     /* creating with UDS_SETBUDDYINT */
845     updown = create_updown_control(UDS_SETBUDDYINT | UDS_ALIGNRIGHT, g_edit);
846     GetWindowTextA(g_edit, text, sizeof(text)/sizeof(CHAR));
847     /* 50 is initial value here */
848     ok(lstrcmpA(text, "50") == 0, "Expected '50', got '%s'\n", text);
849     /* now remove style flag */
850     style = GetWindowLongA(updown, GWL_STYLE);
851     SetWindowLongA(updown, GWL_STYLE, style & ~UDS_SETBUDDYINT);
852     SendMessageA(updown, UDM_SETPOS, 0, 20);
853     GetWindowTextA(g_edit, text, sizeof(text)/sizeof(CHAR));
854     ok(lstrcmpA(text, "20") == 0, "Expected '20', got '%s'\n", text);
855     /* set edit text directly, check position */
856     strcpy(text, "10");
857     SetWindowTextA(g_edit, text);
858     ret = SendMessageA(updown, UDM_GETPOS, 0, 0);
859     expect(10, ret);
860     strcpy(text, "11");
861     SetWindowTextA(g_edit, text);
862     ret = SendMessageA(updown, UDM_GETPOS, 0, 0);
863     expect(11, LOWORD(ret));
864     expect(0,  HIWORD(ret));
865     /* set to invalid value */
866     strcpy(text, "21st");
867     SetWindowTextA(g_edit, text);
868     ret = SendMessageA(updown, UDM_GETPOS, 0, 0);
869     expect(11, LOWORD(ret));
870     expect(TRUE, HIWORD(ret));
871     /* set style back */
872     style = GetWindowLongA(updown, GWL_STYLE);
873     SetWindowLongA(updown, GWL_STYLE, style | UDS_SETBUDDYINT);
874     SendMessageA(updown, UDM_SETPOS, 0, 30);
875     GetWindowTextA(g_edit, text, sizeof(text)/sizeof(CHAR));
876     ok(lstrcmpA(text, "30") == 0, "Expected '30', got '%s'\n", text);
877     DestroyWindow(updown);
878 }
879 
880 static void test_CreateUpDownControl(void)
881 {
882     HWND updown, buddy;
883     DWORD range, pos;
884     RECT rect;
885 
886     GetClientRect(parent_wnd, &rect);
887     updown = pCreateUpDownControl(WS_CHILD | WS_BORDER | WS_VISIBLE,
888         0, 0, rect.right, rect.bottom, parent_wnd, 1, GetModuleHandleA(NULL), g_edit, 100, 10, 50);
889     ok(updown != NULL, "Failed to create control.\n");
890 
891     buddy = (HWND)SendMessageA(updown, UDM_GETBUDDY, 0, 0);
892     ok(buddy == g_edit, "Unexpected buddy window.\n");
893 
894     range = SendMessageA(updown, UDM_GETRANGE, 0, 0);
895     ok(range == MAKELONG(100, 10), "Unexpected range.\n");
896 
897     pos = SendMessageA(updown, UDM_GETPOS, 0, 0);
898     ok(pos == MAKELONG(50, 1), "Unexpected position.\n");
899 
900     DestroyWindow(updown);
901 }
902 
903 static void init_functions(void)
904 {
905     HMODULE hComCtl32 = LoadLibraryA("comctl32.dll");
906 
907 #define X(f) p##f = (void*)GetProcAddress(hComCtl32, #f);
908 #define X2(f, ord) p##f = (void*)GetProcAddress(hComCtl32, (const char *)ord);
909     X(CreateUpDownControl);
910     X2(SetWindowSubclass, 410);
911 #undef X
912 #undef X2
913 }
914 
915 START_TEST(updown)
916 {
917     init_functions();
918 
919     init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
920 
921     parent_wnd = create_parent_window();
922     ok(parent_wnd != NULL, "Failed to create parent window!\n");
923     g_edit = create_edit_control();
924     ok(g_edit != NULL, "Failed to create edit control\n");
925 
926     test_updown_create();
927     test_updown_pos();
928     test_updown_pos32();
929     test_updown_buddy();
930     test_updown_base();
931     test_updown_unicode();
932     test_UDS_SETBUDDYINT();
933     test_CreateUpDownControl();
934 
935     DestroyWindow(g_edit);
936     DestroyWindow(parent_wnd);
937 }
938