1 /* Unit tests for the progress bar control.
2  *
3  * Copyright 2005 Michael Kaufmann
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 <stdarg.h>
21 
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25 #include "winuser.h"
26 #include "commctrl.h"
27 
28 #include "wine/test.h"
29 
30 #include "v6util.h"
31 
32 static HWND hProgressParentWnd;
33 static const char progressTestClass[] = "ProgressBarTestClass";
34 static BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
35 
create_progress(DWORD style)36 static HWND create_progress(DWORD style)
37 {
38     return CreateWindowExA(0, PROGRESS_CLASSA, "", WS_VISIBLE | style,
39       0, 0, 100, 20, NULL, NULL, GetModuleHandleA(NULL), 0);
40 }
41 
42 /* try to make sure pending X events have been processed before continuing */
flush_events(void)43 static void flush_events(void)
44 {
45     MSG msg;
46     int diff = 100;
47     DWORD time = GetTickCount() + diff;
48 
49     while (diff > 0)
50     {
51         if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min(10,diff), QS_ALLINPUT ) == WAIT_TIMEOUT) break;
52         while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
53         diff = time - GetTickCount();
54     }
55 }
56 
progress_test_wnd_proc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)57 static LRESULT CALLBACK progress_test_wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
58 {
59     switch(msg) {
60 
61     case WM_DESTROY:
62         PostQuitMessage(0);
63         break;
64 
65     default:
66         return DefWindowProcA(hWnd, msg, wParam, lParam);
67     }
68 
69     return 0L;
70 }
71 
72 static WNDPROC progress_wndproc;
73 static BOOL erased;
74 static RECT last_paint_rect;
75 
progress_subclass_proc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)76 static LRESULT CALLBACK progress_subclass_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
77 {
78     if (msg == WM_PAINT)
79     {
80         GetUpdateRect(hWnd, &last_paint_rect, FALSE);
81     }
82     else if (msg == WM_ERASEBKGND)
83     {
84         erased = TRUE;
85     }
86     return CallWindowProcA(progress_wndproc, hWnd, msg, wParam, lParam);
87 }
88 
89 
update_window(HWND hWnd)90 static void update_window(HWND hWnd)
91 {
92     UpdateWindow(hWnd);
93     ok(!GetUpdateRect(hWnd, NULL, FALSE), "GetUpdateRect must return zero after UpdateWindow\n");
94 }
95 
96 
init(void)97 static void init(void)
98 {
99     WNDCLASSA wc;
100     RECT rect;
101     BOOL ret;
102 
103     wc.style = CS_HREDRAW | CS_VREDRAW;
104     wc.cbClsExtra = 0;
105     wc.cbWndExtra = 0;
106     wc.hInstance = GetModuleHandleA(NULL);
107     wc.hIcon = NULL;
108     wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
109     wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
110     wc.lpszMenuName = NULL;
111     wc.lpszClassName = progressTestClass;
112     wc.lpfnWndProc = progress_test_wnd_proc;
113     RegisterClassA(&wc);
114 
115     SetRect(&rect, 0, 0, 400, 20);
116     ret = AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
117     ok(ret, "got %d\n", ret);
118 
119     hProgressParentWnd = CreateWindowExA(0, progressTestClass, "Progress Bar Test", WS_OVERLAPPEDWINDOW,
120       CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, GetModuleHandleA(NULL), 0);
121     ok(hProgressParentWnd != NULL, "failed to create parent wnd\n");
122 
123 }
124 
cleanup(void)125 static void cleanup(void)
126 {
127     MSG msg;
128 
129     PostMessageA(hProgressParentWnd, WM_CLOSE, 0, 0);
130     while (GetMessageA(&msg,0,0,0)) {
131         TranslateMessage(&msg);
132         DispatchMessageA(&msg);
133     }
134 
135     UnregisterClassA(progressTestClass, GetModuleHandleA(NULL));
136 }
137 
138 
139 /*
140  * Tests if a progress bar repaints itself immediately when it receives
141  * some specific messages.
142  */
test_redraw(void)143 static void test_redraw(void)
144 {
145     RECT client_rect, rect;
146     HWND hProgressWnd;
147     LRESULT ret;
148 
149     GetClientRect(hProgressParentWnd, &rect);
150     hProgressWnd = CreateWindowExA(0, PROGRESS_CLASSA, "", WS_CHILD | WS_VISIBLE,
151       0, 0, rect.right, rect.bottom, hProgressParentWnd, NULL, GetModuleHandleA(NULL), 0);
152     ok(hProgressWnd != NULL, "Failed to create progress bar.\n");
153     progress_wndproc = (WNDPROC)SetWindowLongPtrA(hProgressWnd, GWLP_WNDPROC, (LPARAM)progress_subclass_proc);
154 
155     ShowWindow(hProgressParentWnd, SW_SHOWNORMAL);
156     ok(GetUpdateRect(hProgressParentWnd, NULL, FALSE), "GetUpdateRect: There should be a region that needs to be updated\n");
157     flush_events();
158     update_window(hProgressParentWnd);
159 
160     SendMessageA(hProgressWnd, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
161     SendMessageA(hProgressWnd, PBM_SETPOS, 10, 0);
162     SendMessageA(hProgressWnd, PBM_SETSTEP, 20, 0);
163     update_window(hProgressWnd);
164 
165     /* PBM_SETPOS */
166     ok(SendMessageA(hProgressWnd, PBM_SETPOS, 50, 0) == 10, "PBM_SETPOS must return the previous position\n");
167     ok(!GetUpdateRect(hProgressWnd, NULL, FALSE), "PBM_SETPOS: The progress bar should be redrawn immediately\n");
168 
169     /* PBM_DELTAPOS */
170     ok(SendMessageA(hProgressWnd, PBM_DELTAPOS, 15, 0) == 50, "PBM_DELTAPOS must return the previous position\n");
171     ok(!GetUpdateRect(hProgressWnd, NULL, FALSE), "PBM_DELTAPOS: The progress bar should be redrawn immediately\n");
172 
173     /* PBM_SETPOS */
174     ok(SendMessageA(hProgressWnd, PBM_SETPOS, 80, 0) == 65, "PBM_SETPOS must return the previous position\n");
175     ok(!GetUpdateRect(hProgressWnd, NULL, FALSE), "PBM_SETPOS: The progress bar should be redrawn immediately\n");
176 
177     /* PBM_STEPIT */
178     ok(SendMessageA(hProgressWnd, PBM_STEPIT, 0, 0) == 80, "PBM_STEPIT must return the previous position\n");
179     ok(!GetUpdateRect(hProgressWnd, NULL, FALSE), "PBM_STEPIT: The progress bar should be redrawn immediately\n");
180     ret = SendMessageA(hProgressWnd, PBM_GETPOS, 0, 0);
181     if (ret == 0)
182         win_skip("PBM_GETPOS needs comctl32 > 4.70\n");
183     else
184         ok(ret == 100, "PBM_GETPOS returned a wrong position : %d\n", (UINT)ret);
185 
186     /* PBM_SETRANGE and PBM_SETRANGE32:
187     Usually the progress bar doesn't repaint itself immediately. If the
188     position is not in the new range, it does.
189     Don't test this, it may change in future Windows versions. */
190 
191     SendMessageA(hProgressWnd, PBM_SETPOS, 0, 0);
192     update_window(hProgressWnd);
193 
194     /* increase to 10 - no background erase required */
195     erased = FALSE;
196     SetRectEmpty(&last_paint_rect);
197     SendMessageA(hProgressWnd, PBM_SETPOS, 10, 0);
198     GetClientRect(hProgressWnd, &client_rect);
199     ok(EqualRect(&last_paint_rect, &client_rect), "last_paint_rect was %s instead of %s\n",
200        wine_dbgstr_rect(&last_paint_rect), wine_dbgstr_rect(&client_rect));
201     update_window(hProgressWnd);
202     ok(!erased, "Progress bar shouldn't have erased the background\n");
203 
204     /* decrease to 0 - background erase will be required */
205     erased = FALSE;
206     SetRectEmpty(&last_paint_rect);
207     SendMessageA(hProgressWnd, PBM_SETPOS, 0, 0);
208     GetClientRect(hProgressWnd, &client_rect);
209     ok(EqualRect(&last_paint_rect, &client_rect), "last_paint_rect was %s instead of %s\n",
210        wine_dbgstr_rect(&last_paint_rect), wine_dbgstr_rect(&client_rect));
211     update_window(hProgressWnd);
212     ok(erased, "Progress bar should have erased the background\n");
213 
214     DestroyWindow(hProgressWnd);
215 }
216 
test_setcolors(void)217 static void test_setcolors(void)
218 {
219     HWND progress;
220     COLORREF clr;
221 
222     progress = create_progress(PBS_SMOOTH);
223 
224     clr = SendMessageA(progress, PBM_SETBARCOLOR, 0, 0);
225     ok(clr == CLR_DEFAULT, "got %x\n", clr);
226 
227     clr = SendMessageA(progress, PBM_SETBARCOLOR, 0, RGB(0, 255, 0));
228     ok(clr == 0, "got %x\n", clr);
229 
230     clr = SendMessageA(progress, PBM_SETBARCOLOR, 0, CLR_DEFAULT);
231     ok(clr == RGB(0, 255, 0), "got %x\n", clr);
232 
233     clr = SendMessageA(progress, PBM_SETBKCOLOR, 0, 0);
234     ok(clr == CLR_DEFAULT, "got %x\n", clr);
235 
236     clr = SendMessageA(progress, PBM_SETBKCOLOR, 0, RGB(255, 0, 0));
237     ok(clr == 0, "got %x\n", clr);
238 
239     clr = SendMessageA(progress, PBM_SETBKCOLOR, 0, CLR_DEFAULT);
240     ok(clr == RGB(255, 0, 0), "got %x\n", clr);
241 
242     DestroyWindow(progress);
243 }
244 
test_PBM_STEPIT(void)245 static void test_PBM_STEPIT(void)
246 {
247     struct stepit_test
248     {
249         int min;
250         int max;
251         int step;
252     } stepit_tests[] =
253     {
254         { 3, 15,  5 },
255         { 3, 15, -5 },
256         { 3, 15, 50 },
257         { -15, 15,  5 },
258         { -3, -2, -5 },
259         { 0, 0, 1 },
260         { 5, 5, 1 },
261         { 0, 0, -1 },
262         { 5, 5, -1 },
263         { 10, 5, 2 },
264     };
265     HWND progress;
266     int i, j;
267 
268     for (i = 0; i < ARRAY_SIZE(stepit_tests); i++)
269     {
270         struct stepit_test *test = &stepit_tests[i];
271         PBRANGE range;
272         LRESULT ret;
273 
274         progress = create_progress(0);
275 
276         ret = SendMessageA(progress, PBM_SETRANGE32, test->min, test->max);
277         ok(ret != 0, "Unexpected return value.\n");
278 
279         SendMessageA(progress, PBM_GETRANGE, 0, (LPARAM)&range);
280         ok(range.iLow == test->min && range.iHigh == test->max, "Unexpected range.\n");
281 
282         SendMessageA(progress, PBM_SETPOS, test->min, 0);
283         SendMessageA(progress, PBM_SETSTEP, test->step, 0);
284 
285         for (j = 0; j < test->max; j++)
286         {
287             int pos = SendMessageA(progress, PBM_GETPOS, 0, 0);
288             int current;
289 
290             pos += test->step;
291             if (test->min != test->max)
292             {
293                 if (pos > test->max)
294                     pos = (pos - test->min) % (test->max - test->min) + test->min;
295                 if (pos < test->min)
296                     pos = (pos - test->min) % (test->max - test->min) + test->max;
297             }
298             else
299                 pos = test->min;
300 
301             SendMessageA(progress, PBM_STEPIT, 0, 0);
302 
303             current = SendMessageA(progress, PBM_GETPOS, 0, 0);
304             ok(current == pos, "%u: unexpected position %d, expected %d.\n", i, current, pos);
305         }
306 
307         DestroyWindow(progress);
308     }
309 }
310 
init_functions(void)311 static void init_functions(void)
312 {
313     HMODULE hComCtl32 = LoadLibraryA("comctl32.dll");
314 
315 #define X(f) p##f = (void*)GetProcAddress(hComCtl32, #f);
316     X(InitCommonControlsEx);
317 #undef X
318 }
319 
START_TEST(progress)320 START_TEST(progress)
321 {
322     INITCOMMONCONTROLSEX iccex;
323     ULONG_PTR ctx_cookie;
324     HANDLE hCtx;
325 
326     init_functions();
327 
328     iccex.dwSize = sizeof(iccex);
329     iccex.dwICC  = ICC_PROGRESS_CLASS;
330     pInitCommonControlsEx(&iccex);
331 
332     init();
333 
334     test_redraw();
335     test_setcolors();
336     test_PBM_STEPIT();
337 
338     if (!load_v6_module(&ctx_cookie, &hCtx))
339         return;
340 
341     test_setcolors();
342     test_PBM_STEPIT();
343 
344     unload_v6_module(ctx_cookie, hCtx);
345 
346     cleanup();
347 }
348