2 * comctl32 MRU unit tests
4 * Copyright (C) 2004 Jon Griffiths <jon_p_griffiths@yahoo.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/test.h"
33 /* Keys for testing MRU functions */
34 #define REG_TEST_BASEKEYA "Software\\Wine"
35 #define REG_TEST_BASESUBKEYA "Test"
36 #define REG_TEST_KEYA REG_TEST_BASEKEYA "\\" REG_TEST_BASESUBKEYA
37 #define REG_TEST_SUBKEYA "MRUTest"
38 #define REG_TEST_FULLKEY REG_TEST_KEYA "\\" REG_TEST_SUBKEYA
40 /* Undocumented MRU structures & functions */
41 typedef struct tagCREATEMRULISTA
49 } CREATEMRULISTA
, *LPCREATEMRULISTA
;
51 #define MRUF_STRING_LIST 0
52 #define MRUF_BINARY_LIST 1
53 #define MRUF_DELAYED_SAVE 2
55 #define LIST_SIZE 3 /* Max entries for each mru */
57 static CREATEMRULISTA mruA
=
59 sizeof(CREATEMRULISTA
),
67 static HMODULE hComctl32
;
68 static HANDLE (WINAPI
*pCreateMRUListA
)(LPCREATEMRULISTA
);
69 static void (WINAPI
*pFreeMRUList
)(HANDLE
);
70 static INT (WINAPI
*pAddMRUStringA
)(HANDLE
,LPCSTR
);
71 static INT (WINAPI
*pEnumMRUList
)(HANDLE
,INT
,LPVOID
,DWORD
);
73 static INT (WINAPI *pFindMRUStringA)(HANDLE,LPCSTR,LPINT);
77 /* Based on RegDeleteTreeW from dlls/advapi32/registry.c */
78 static LSTATUS
mru_RegDeleteTreeA(HKEY hKey
, LPCSTR lpszSubKey
)
81 DWORD dwMaxSubkeyLen
, dwMaxValueLen
;
82 DWORD dwMaxLen
, dwSize
;
83 CHAR szNameBuf
[MAX_PATH
], *lpszName
= szNameBuf
;
88 ret
= RegOpenKeyExA(hKey
, lpszSubKey
, 0, KEY_READ
, &hSubKey
);
92 /* Get highest length for keys, values */
93 ret
= RegQueryInfoKeyA(hSubKey
, NULL
, NULL
, NULL
, NULL
,
94 &dwMaxSubkeyLen
, NULL
, NULL
, &dwMaxValueLen
, NULL
, NULL
, NULL
);
95 if (ret
) goto cleanup
;
99 dwMaxLen
= max(dwMaxSubkeyLen
, dwMaxValueLen
);
100 if (dwMaxLen
> sizeof(szNameBuf
)/sizeof(CHAR
))
102 /* Name too big: alloc a buffer for it */
103 if (!(lpszName
= HeapAlloc( GetProcessHeap(), 0, dwMaxLen
*sizeof(CHAR
))))
105 ret
= ERROR_NOT_ENOUGH_MEMORY
;
111 /* Recursively delete all the subkeys */
115 if (RegEnumKeyExA(hSubKey
, 0, lpszName
, &dwSize
, NULL
,
116 NULL
, NULL
, NULL
)) break;
118 ret
= mru_RegDeleteTreeA(hSubKey
, lpszName
);
119 if (ret
) goto cleanup
;
123 ret
= RegDeleteKeyA(hKey
, lpszSubKey
);
128 if (RegEnumValueA(hKey
, 0, lpszName
, &dwSize
,
129 NULL
, NULL
, NULL
, NULL
)) break;
131 ret
= RegDeleteValueA(hKey
, lpszName
);
132 if (ret
) goto cleanup
;
136 /* Free buffer if allocated */
137 if (lpszName
!= szNameBuf
)
138 HeapFree( GetProcessHeap(), 0, lpszName
);
140 RegCloseKey(hSubKey
);
144 static BOOL
create_reg_entries(void)
148 ok(!RegCreateKeyA(HKEY_CURRENT_USER
, REG_TEST_FULLKEY
, &hKey
),
149 "Couldn't create test key \"%s\"\n", REG_TEST_KEYA
);
150 if (!hKey
) return FALSE
;
155 static void delete_reg_entries(void)
159 if (RegOpenKeyExA(HKEY_CURRENT_USER
, REG_TEST_BASEKEYA
, 0, KEY_ALL_ACCESS
,
162 mru_RegDeleteTreeA(hKey
, REG_TEST_BASESUBKEYA
);
166 static void check_reg_entries(const char *mrulist
, const char**items
)
170 DWORD type
, size
, ret
;
173 ok(!RegOpenKeyA(HKEY_CURRENT_USER
, REG_TEST_FULLKEY
, &hKey
),
174 "Couldn't open test key \"%s\"\n", REG_TEST_FULLKEY
);
180 ret
= RegQueryValueExA(hKey
, "MRUList", NULL
, &type
, (LPBYTE
)buff
, &size
);
182 ok(!ret
&& buff
[0], "Checking MRU: got %d from RegQueryValueExW\n", ret
);
183 if(ret
|| !buff
[0]) return;
185 ok(strcmp(buff
, mrulist
) == 0, "Checking MRU: Expected list %s, got %s\n",
187 if(strcmp(buff
, mrulist
)) return;
189 for (i
= 0; i
< strlen(mrulist
); i
++)
192 name
[0] = mrulist
[i
];
197 ret
= RegQueryValueExA(hKey
, name
, NULL
, &type
, (LPBYTE
)buff
, &size
);
199 "Checking MRU item %d ('%c'): got %d from RegQueryValueExW\n",
201 if(ret
|| !buff
[0]) return;
202 ok(!strcmp(buff
, items
[mrulist
[i
]-'a']),
203 "Checking MRU item %d ('%c'): expected \"%s\", got \"%s\"\n",
204 i
, mrulist
[i
], buff
, items
[mrulist
[i
] - 'a']);
208 static INT CALLBACK
cmp_mru_strA(LPCVOID data1
, LPCVOID data2
)
210 return lstrcmpiA(data1
, data2
);
213 static HANDLE
create_mruA(HKEY hKey
, DWORD flags
, PROC cmp
)
215 mruA
.dwFlags
= flags
;
216 mruA
.lpfnCompare
= cmp
;
220 return pCreateMRUListA(&mruA
);
223 static void test_MRUListA(void)
225 const char *checks
[LIST_SIZE
+1];
230 pCreateMRUListA
= (void*)GetProcAddress(hComctl32
,(LPCSTR
)151);
231 pFreeMRUList
= (void*)GetProcAddress(hComctl32
,(LPCSTR
)152);
232 pAddMRUStringA
= (void*)GetProcAddress(hComctl32
,(LPCSTR
)153);
233 pEnumMRUList
= (void*)GetProcAddress(hComctl32
,(LPCSTR
)154);
235 if (!pCreateMRUListA
|| !pFreeMRUList
|| !pAddMRUStringA
|| !pEnumMRUList
)
237 skip("MRU entry points not found\n");
243 /* Create (NULL) - crashes native */
244 hMRU
= pCreateMRUListA(NULL
);
247 /* Create (size too small) */
248 mruA
.cbSize
= sizeof(mruA
) - 2;
249 hMRU
= create_mruA(NULL
, MRUF_STRING_LIST
, (PROC
)cmp_mru_strA
);
250 ok (!hMRU
&& !GetLastError(),
251 "CreateMRUListA(too small) expected NULL,0 got %p,%d\n",
252 hMRU
, GetLastError());
253 mruA
.cbSize
= sizeof(mruA
);
255 /* Create (size too big) */
256 mruA
.cbSize
= sizeof(mruA
) + 2;
257 hMRU
= create_mruA(NULL
, MRUF_STRING_LIST
, (PROC
)cmp_mru_strA
);
258 ok (!hMRU
&& !GetLastError(),
259 "CreateMRUListA(too big) expected NULL,0 got %p,%d\n",
260 hMRU
, GetLastError());
261 mruA
.cbSize
= sizeof(mruA
);
263 /* Create (NULL hKey) */
264 hMRU
= create_mruA(NULL
, MRUF_STRING_LIST
, (PROC
)cmp_mru_strA
);
265 ok (!hMRU
&& !GetLastError(),
266 "CreateMRUListA(NULL key) expected NULL,0 got %p,%d\n",
267 hMRU
, GetLastError());
269 /* Create (NULL name) */
270 mruA
.lpszSubKey
= NULL
;
271 hMRU
= create_mruA(NULL
, MRUF_STRING_LIST
, (PROC
)cmp_mru_strA
);
272 ok (!hMRU
&& !GetLastError(),
273 "CreateMRUListA(NULL name) expected NULL,0 got %p,%d\n",
274 hMRU
, GetLastError());
275 mruA
.lpszSubKey
= REG_TEST_SUBKEYA
;
277 /* Create a string MRU */
278 ok(!RegCreateKeyA(HKEY_CURRENT_USER
, REG_TEST_KEYA
, &hKey
),
279 "Couldn't create test key \"%s\"\n", REG_TEST_KEYA
);
282 hMRU
= create_mruA(hKey
, MRUF_STRING_LIST
, (PROC
)cmp_mru_strA
);
283 ok(hMRU
&& !GetLastError(),
284 "CreateMRUListA(string) expected non-NULL,0 got %p,%d\n",
285 hMRU
, GetLastError());
290 checks
[0] = "Test 1";
291 checks
[1] = "Test 2";
292 checks
[2] = "Test 3";
293 checks
[3] = "Test 4";
295 /* Add (NULL list) */
297 iRet
= pAddMRUStringA(NULL
, checks
[0]);
298 ok(iRet
== -1 && !GetLastError(),
299 "AddMRUStringA(NULL list) expected -1,0 got %d,%d\n",
300 iRet
, GetLastError());
302 /* Add (NULL string) */
305 /* Some native versions crash when passed NULL or fail to SetLastError() */
307 iRet
= pAddMRUStringA(hMRU
, NULL
);
308 ok(iRet
== 0 && GetLastError() == ERROR_INVALID_PARAMETER
,
309 "AddMRUStringA(NULL str) expected 0,ERROR_INVALID_PARAMETER got %d,%d\n",
310 iRet
, GetLastError());
313 /* Add 3 strings. Check the registry is correct after each add */
315 iRet
= pAddMRUStringA(hMRU
, checks
[0]);
316 ok(iRet
== 0 && !GetLastError(),
317 "AddMRUStringA(1) expected 0,0 got %d,%d\n",
318 iRet
, GetLastError());
319 check_reg_entries("a", checks
);
322 iRet
= pAddMRUStringA(hMRU
, checks
[1]);
323 ok(iRet
== 1 && !GetLastError(),
324 "AddMRUStringA(2) expected 1,0 got %d,%d\n",
325 iRet
, GetLastError());
326 check_reg_entries("ba", checks
);
329 iRet
= pAddMRUStringA(hMRU
, checks
[2]);
330 ok(iRet
== 2 && !GetLastError(),
331 "AddMRUStringA(2) expected 2,0 got %d,%d\n",
332 iRet
, GetLastError());
333 check_reg_entries("cba", checks
);
335 /* Add a duplicate of the 2nd string - it should move to the front,
336 * but keep the same index in the registry.
339 iRet
= pAddMRUStringA(hMRU
, checks
[1]);
340 ok(iRet
== 1 && !GetLastError(),
341 "AddMRUStringA(re-add 1) expected 1,0 got %d,%d\n",
342 iRet
, GetLastError());
343 check_reg_entries("bca", checks
);
345 /* Add a new string - replaces the oldest string + moves to the front */
347 iRet
= pAddMRUStringA(hMRU
, checks
[3]);
348 ok(iRet
== 0 && !GetLastError(),
349 "AddMRUStringA(add new) expected 0,0 got %d,%d\n",
350 iRet
, GetLastError());
351 checks
[0] = checks
[3];
352 check_reg_entries("abc", checks
);
354 /* NULL buffer = get list size */
355 iRet
= pEnumMRUList(hMRU
, 0, NULL
, 0);
356 ok(iRet
== 3 || iRet
== -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE
, iRet
);
358 /* negative item pos = get list size */
359 iRet
= pEnumMRUList(hMRU
, -1, NULL
, 0);
360 ok(iRet
== 3 || iRet
== -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE
, iRet
);
362 /* negative item pos = get list size */
363 iRet
= pEnumMRUList(hMRU
, -5, NULL
, 0);
364 ok(iRet
== 3 || iRet
== -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE
, iRet
);
366 /* negative item pos = get list size */
367 iRet
= pEnumMRUList(hMRU
, -1, buffer
, 255);
368 ok(iRet
== 3, "EnumMRUList expected %d, got %d\n", LIST_SIZE
, iRet
);
370 /* negative item pos = get list size */
371 iRet
= pEnumMRUList(hMRU
, -5, buffer
, 255);
372 ok(iRet
== 3, "EnumMRUList expected %d, got %d\n", LIST_SIZE
, iRet
);
376 iRet
= pEnumMRUList(hMRU
, 0, buffer
, 255);
377 todo_wine
ok(iRet
== lstrlen(checks
[3]), "EnumMRUList expected %d, got %d\n", lstrlen(checks
[3]), iRet
);
378 ok(strcmp(buffer
, checks
[3]) == 0, "EnumMRUList expected %s, got %s\n", checks
[3], buffer
);
380 /* check entry 0 with a too small buffer */
381 buffer
[0] = 0; /* overwritten with 'T' */
382 buffer
[1] = 'A'; /* overwritten with 0 */
383 buffer
[2] = 'A'; /* unchanged */
384 buffer
[3] = 0; /* unchanged */
385 iRet
= pEnumMRUList(hMRU
, 0, buffer
, 2);
386 todo_wine
ok(iRet
== lstrlen(checks
[3]), "EnumMRUList expected %d, got %d\n", lstrlen(checks
[3]), iRet
);
387 todo_wine
ok(strcmp(buffer
, "T") == 0, "EnumMRUList expected %s, got %s\n", "T", buffer
);
388 /* make sure space after buffer has old values */
389 ok(buffer
[2] == 'A', "EnumMRUList expected %02x, got %02x\n", 'A', buffer
[2]);
393 iRet
= pEnumMRUList(hMRU
, 1, buffer
, 255);
394 todo_wine
ok(iRet
== lstrlen(checks
[1]), "EnumMRUList expected %d, got %d\n", lstrlen(checks
[1]), iRet
);
395 ok(strcmp(buffer
, checks
[1]) == 0, "EnumMRUList expected %s, got %s\n", checks
[1], buffer
);
399 iRet
= pEnumMRUList(hMRU
, 2, buffer
, 255);
400 todo_wine
ok(iRet
== lstrlen(checks
[2]), "EnumMRUList expected %d, got %d\n", lstrlen(checks
[2]), iRet
);
401 ok(strcmp(buffer
, checks
[2]) == 0, "EnumMRUList expected %s, got %s\n", checks
[2], buffer
);
403 /* check out of bounds entry 3 */
404 strcpy(buffer
, "dummy");
405 iRet
= pEnumMRUList(hMRU
, 3, buffer
, 255);
406 ok(iRet
== -1, "EnumMRUList expected %d, got %d\n", -1, iRet
);
407 ok(strcmp(buffer
, "dummy") == 0, "EnumMRUList expected unchanged buffer %s, got %s\n", "dummy", buffer
);
409 /* Finished with this MRU */
413 /* Free (NULL list) - Doesn't crash */
419 hComctl32
= GetModuleHandleA("comctl32.dll");
421 delete_reg_entries();
422 if (!create_reg_entries())
427 delete_reg_entries();