1 /* Unit tests for the task dialog control.
3 * Copyright 2017 Fabian Maurer for the Wine project
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.
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.
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
27 #include "wine/heap.h"
28 #include "wine/test.h"
32 #define WM_TD_CALLBACK (WM_APP) /* Custom dummy message to wrap callback notifications */
34 #define NUM_MSG_SEQUENCES 1
35 #define TASKDIALOG_SEQ_INDEX 0
37 #define TEST_NUM_BUTTONS 10 /* Number of custom buttons to test with */
39 #define ID_START 20 /* Lower IDs might be used by the system */
40 #define ID_START_BUTTON (ID_START + 0)
42 static HRESULT (WINAPI
*pTaskDialogIndirect
)(const TASKDIALOGCONFIG
*, int *, int *, BOOL
*);
43 static HRESULT (WINAPI
*pTaskDialog
)(HWND
, HINSTANCE
, const WCHAR
*, const WCHAR
*, const WCHAR
*,
44 TASKDIALOG_COMMON_BUTTON_FLAGS
, const WCHAR
*, int *);
46 static struct msg_sequence
*sequences
[NUM_MSG_SEQUENCES
];
54 HRESULT callback_retval
;
55 const struct message_info
*send
; /* Message to send to trigger the next callback message */
58 static const struct message_info
*current_message_info
;
60 /* Messages to send */
61 static const struct message_info msg_send_return
[] =
63 { WM_KEYDOWN
, VK_RETURN
, 0 },
67 /* Messages to test against */
68 static const struct message_info msg_return_press_ok
[] =
70 { TDN_CREATED
, 0, 0, S_OK
, msg_send_return
},
71 { TDN_BUTTON_CLICKED
, IDOK
, 0, S_OK
, NULL
},
75 static const struct message_info msg_return_press_yes
[] =
77 { TDN_CREATED
, 0, 0, S_OK
, msg_send_return
},
78 { TDN_BUTTON_CLICKED
, IDYES
, 0, S_OK
, NULL
},
82 static const struct message_info msg_return_press_no
[] =
84 { TDN_CREATED
, 0, 0, S_OK
, msg_send_return
},
85 { TDN_BUTTON_CLICKED
, IDNO
, 0, S_OK
, NULL
},
89 static const struct message_info msg_return_press_cancel
[] =
91 { TDN_CREATED
, 0, 0, S_OK
, msg_send_return
},
92 { TDN_BUTTON_CLICKED
, IDCANCEL
, 0, S_OK
, NULL
},
96 static const struct message_info msg_return_press_retry
[] =
98 { TDN_CREATED
, 0, 0, S_OK
, msg_send_return
},
99 { TDN_BUTTON_CLICKED
, IDRETRY
, 0, S_OK
, NULL
},
103 static const struct message_info msg_return_press_custom1
[] =
105 { TDN_CREATED
, 0, 0, S_OK
, msg_send_return
},
106 { TDN_BUTTON_CLICKED
, ID_START_BUTTON
, 0, S_OK
, NULL
},
110 static const struct message_info msg_return_press_custom4
[] =
112 { TDN_CREATED
, 0, 0, S_OK
, msg_send_return
},
113 { TDN_BUTTON_CLICKED
, ID_START_BUTTON
+ 3, 0, S_OK
, NULL
},
117 static const struct message_info msg_return_press_custom10
[] =
119 { TDN_CREATED
, 0, 0, S_OK
, msg_send_return
},
120 { TDN_BUTTON_CLICKED
, -1, 0, S_OK
, NULL
},
124 static void init_test_message(UINT message
, WPARAM wParam
, LPARAM lParam
, struct message
*msg
)
126 msg
->message
= WM_TD_CALLBACK
;
127 msg
->flags
= sent
|wparam
|lparam
|id
;
128 msg
->wParam
= wParam
;
129 msg
->lParam
= lParam
;
134 #define run_test(info, expect_button, seq, context) \
135 run_test_(info, expect_button, seq, context, \
136 sizeof(seq)/sizeof(seq[0]) - 1, __FILE__, __LINE__)
138 static void run_test_(TASKDIALOGCONFIG
*info
, int expect_button
, const struct message_info
*test_messages
,
139 const char *context
, int test_messages_len
, const char *file
, int line
)
141 struct message
*msg
, *msg_start
;
147 /* Allocate messages to test against, plus 2 implicit and 1 empty */
148 msg_start
= msg
= heap_alloc_zero(sizeof(*msg
) * (test_messages_len
+ 3));
150 /* Always needed, thus made implicit */
151 init_test_message(TDN_DIALOG_CONSTRUCTED
, 0, 0, msg
++);
152 for (i
= 0; i
< test_messages_len
; i
++)
153 init_test_message(test_messages
[i
].message
, test_messages
[i
].wparam
, test_messages
[i
].lparam
, msg
++);
154 /* Always needed, thus made implicit */
155 init_test_message(TDN_DESTROYED
, 0, 0, msg
++);
157 current_message_info
= test_messages
;
158 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
160 hr
= pTaskDialogIndirect(info
, &ret_button
, &ret_radio
, NULL
);
161 ok_(file
, line
)(hr
== S_OK
, "TaskDialogIndirect() failed, got %#x.\n", hr
);
163 ok_sequence_(sequences
, TASKDIALOG_SEQ_INDEX
, msg_start
, context
, FALSE
, file
, line
);
164 ok_(file
, line
)(ret_button
== expect_button
,
165 "Wrong button. Expected %d, got %d\n", expect_button
, ret_button
);
167 heap_free(msg_start
);
170 static const LONG_PTR test_ref_data
= 123456;
172 static HRESULT CALLBACK
taskdialog_callback_proc(HWND hwnd
, UINT notification
,
173 WPARAM wParam
, LPARAM lParam
, LONG_PTR ref_data
)
175 int msg_pos
= sequences
[TASKDIALOG_SEQ_INDEX
]->count
- 1; /* Skip implicit message */
176 const struct message_info
*msg_send
;
179 ok(test_ref_data
== ref_data
, "Unexpected ref data %lu.\n", ref_data
);
181 init_test_message(notification
, (short)wParam
, lParam
, &msg
);
182 add_message(sequences
, TASKDIALOG_SEQ_INDEX
, &msg
);
184 if (notification
== TDN_DIALOG_CONSTRUCTED
|| notification
== TDN_DESTROYED
) /* Skip implicit messages */
187 msg_send
= current_message_info
[msg_pos
].send
;
188 for(; msg_send
&& msg_send
->message
; msg_send
++)
189 PostMessageW(hwnd
, msg_send
->message
, msg_send
->wparam
, msg_send
->lparam
);
191 return current_message_info
[msg_pos
].callback_retval
;
194 static void test_invalid_parameters(void)
196 TASKDIALOGCONFIG info
= { 0 };
199 hr
= pTaskDialogIndirect(NULL
, NULL
, NULL
, NULL
);
200 ok(hr
== E_INVALIDARG
, "Unexpected return value %#x.\n", hr
);
203 hr
= pTaskDialogIndirect(&info
, NULL
, NULL
, NULL
);
204 ok(hr
== E_INVALIDARG
, "Unexpected return value %#x.\n", hr
);
206 info
.cbSize
= sizeof(TASKDIALOGCONFIG
) - 1;
207 hr
= pTaskDialogIndirect(&info
, NULL
, NULL
, NULL
);
208 ok(hr
== E_INVALIDARG
, "Unexpected return value %#x.\n", hr
);
210 info
.cbSize
= sizeof(TASKDIALOGCONFIG
) + 1;
211 hr
= pTaskDialogIndirect(&info
, NULL
, NULL
, NULL
);
212 ok(hr
== E_INVALIDARG
, "Unexpected return value %#x.\n", hr
);
215 static void test_callback(void)
217 TASKDIALOGCONFIG info
= {0};
219 info
.cbSize
= sizeof(TASKDIALOGCONFIG
);
220 info
.pfCallback
= taskdialog_callback_proc
;
221 info
.lpCallbackData
= test_ref_data
;
223 run_test(&info
, IDOK
, msg_return_press_ok
, "Press VK_RETURN.");
226 static void test_buttons(void)
228 TASKDIALOGCONFIG info
= {0};
230 TASKDIALOG_BUTTON custom_buttons
[TEST_NUM_BUTTONS
];
231 const WCHAR button_format
[] = {'%','0','2','d',0};
232 WCHAR button_titles
[TEST_NUM_BUTTONS
* 3]; /* Each button has two digits as title, plus null-terminator */
235 info
.cbSize
= sizeof(TASKDIALOGCONFIG
);
236 info
.pfCallback
= taskdialog_callback_proc
;
237 info
.lpCallbackData
= test_ref_data
;
239 /* Init custom buttons */
240 for (i
= 0; i
< TEST_NUM_BUTTONS
; i
++)
242 WCHAR
*text
= &button_titles
[i
* 3];
243 wsprintfW(text
, button_format
, i
);
245 custom_buttons
[i
].pszButtonText
= text
;
246 custom_buttons
[i
].nButtonID
= ID_START_BUTTON
+ i
;
248 custom_buttons
[TEST_NUM_BUTTONS
- 1].nButtonID
= -1;
250 /* Test nDefaultButton */
252 /* Test common buttons with invalid default ID */
253 info
.nDefaultButton
= 0; /* Should default to first created button */
254 info
.dwCommonButtons
= TDCBF_OK_BUTTON
| TDCBF_YES_BUTTON
| TDCBF_NO_BUTTON
255 | TDCBF_CANCEL_BUTTON
| TDCBF_RETRY_BUTTON
| TDCBF_CLOSE_BUTTON
;
256 run_test(&info
, IDOK
, msg_return_press_ok
, "default button: unset default");
257 info
.dwCommonButtons
= TDCBF_YES_BUTTON
| TDCBF_NO_BUTTON
258 | TDCBF_CANCEL_BUTTON
| TDCBF_RETRY_BUTTON
| TDCBF_CLOSE_BUTTON
;
259 run_test(&info
, IDYES
, msg_return_press_yes
, "default button: unset default");
260 info
.dwCommonButtons
= TDCBF_NO_BUTTON
| TDCBF_CANCEL_BUTTON
| TDCBF_RETRY_BUTTON
| TDCBF_CLOSE_BUTTON
;
261 run_test(&info
, IDNO
, msg_return_press_no
, "default button: unset default");
262 info
.dwCommonButtons
= TDCBF_CANCEL_BUTTON
| TDCBF_RETRY_BUTTON
| TDCBF_CLOSE_BUTTON
;
263 run_test(&info
, IDRETRY
, msg_return_press_retry
, "default button: unset default");
264 info
.dwCommonButtons
= TDCBF_CANCEL_BUTTON
| TDCBF_CLOSE_BUTTON
;
265 run_test(&info
, IDCANCEL
, msg_return_press_cancel
, "default button: unset default");
267 /* Test with all common and custom buttons and invalid default ID */
268 info
.nDefaultButton
= 0xff; /* Random ID, should also default to first created button */
269 info
.cButtons
= TEST_NUM_BUTTONS
;
270 info
.pButtons
= custom_buttons
;
271 run_test(&info
, ID_START_BUTTON
, msg_return_press_custom1
, "default button: invalid default, with common buttons - 1");
273 info
.nDefaultButton
= -1; /* Should work despite button ID -1 */
274 run_test(&info
, -1, msg_return_press_custom10
, "default button: invalid default, with common buttons - 2");
276 info
.nDefaultButton
= -2; /* Should also default to first created button */
277 run_test(&info
, ID_START_BUTTON
, msg_return_press_custom1
, "default button: invalid default, with common buttons - 3");
279 /* Test with only custom buttons and invalid default ID */
280 info
.dwCommonButtons
= 0;
281 run_test(&info
, ID_START_BUTTON
, msg_return_press_custom1
, "default button: invalid default, no common buttons");
283 /* Test with common and custom buttons and valid default ID */
284 info
.dwCommonButtons
= TDCBF_OK_BUTTON
| TDCBF_YES_BUTTON
| TDCBF_NO_BUTTON
285 | TDCBF_CANCEL_BUTTON
| TDCBF_RETRY_BUTTON
| TDCBF_CLOSE_BUTTON
;
286 info
.nDefaultButton
= IDRETRY
;
287 run_test(&info
, IDRETRY
, msg_return_press_retry
, "default button: valid default - 1");
289 /* Test with common and custom buttons and valid default ID */
290 info
.nDefaultButton
= ID_START_BUTTON
+ 3;
291 run_test(&info
, ID_START_BUTTON
+ 3, msg_return_press_custom4
, "default button: valid default - 2");
294 START_TEST(taskdialog
)
296 ULONG_PTR ctx_cookie
;
301 if (!load_v6_module(&ctx_cookie
, &hCtx
))
304 /* Check if task dialogs are available */
305 hinst
= LoadLibraryA("comctl32.dll");
307 pTaskDialogIndirect
= (void *)GetProcAddress(hinst
, "TaskDialogIndirect");
308 if (!pTaskDialogIndirect
)
310 win_skip("TaskDialogIndirect not exported by name\n");
311 unload_v6_module(ctx_cookie
, hCtx
);
315 pTaskDialog
= (void *)GetProcAddress(hinst
, "TaskDialog");
317 ptr_ordinal
= GetProcAddress(hinst
, (const char *)344);
318 ok(pTaskDialog
== ptr_ordinal
, "got wrong pointer for ordinal 344, %p expected %p\n",
319 ptr_ordinal
, pTaskDialog
);
321 ptr_ordinal
= GetProcAddress(hinst
, (const char *)345);
322 ok(pTaskDialogIndirect
== ptr_ordinal
, "got wrong pointer for ordinal 345, %p expected %p\n",
323 ptr_ordinal
, pTaskDialogIndirect
);
325 init_msg_sequences(sequences
, NUM_MSG_SEQUENCES
);
327 test_invalid_parameters();
331 unload_v6_module(ctx_cookie
, hCtx
);