- Hack away mru tests as Cm is broken
[reactos.git] / rostests / winetests / comctl32 / mru.c
1 /*
2 * comctl32 MRU unit tests
3 *
4 * Copyright (C) 2004 Jon Griffiths <jon_p_griffiths@yahoo.com>
5 *
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.
10 *
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.
15 *
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
19 */
20 #include <stdarg.h>
21
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25 #include "winuser.h"
26 #include "winnls.h"
27 #include "winreg.h"
28 #include "commctrl.h"
29 #include "shlwapi.h"
30
31 #include "wine/test.h"
32
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
39
40 /* Undocumented MRU structures & functions */
41 typedef struct tagCREATEMRULISTA
42 {
43 DWORD cbSize;
44 DWORD nMaxItems;
45 DWORD dwFlags;
46 HKEY hKey;
47 LPCSTR lpszSubKey;
48 PROC lpfnCompare;
49 } CREATEMRULISTA, *LPCREATEMRULISTA;
50
51 #define MRUF_STRING_LIST 0
52 #define MRUF_BINARY_LIST 1
53 #define MRUF_DELAYED_SAVE 2
54
55 #define LIST_SIZE 3 /* Max entries for each mru */
56
57 static CREATEMRULISTA mruA =
58 {
59 sizeof(CREATEMRULISTA),
60 LIST_SIZE,
61 0,
62 NULL,
63 REG_TEST_SUBKEYA,
64 NULL
65 };
66
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);
72 /*
73 static INT (WINAPI *pFindMRUStringA)(HANDLE,LPCSTR,LPINT);
74 */
75
76
77 /* Based on RegDeleteTreeW from dlls/advapi32/registry.c */
78 static LONG mru_RegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey)
79 {
80 LONG ret;
81 DWORD dwMaxSubkeyLen, dwMaxValueLen;
82 DWORD dwMaxLen, dwSize;
83 CHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
84 HKEY hSubKey = hKey;
85
86 if(lpszSubKey)
87 {
88 ret = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
89 if (ret) return ret;
90 }
91
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;
96
97 dwMaxSubkeyLen++;
98 dwMaxValueLen++;
99 dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen);
100 if (dwMaxLen > sizeof(szNameBuf)/sizeof(CHAR))
101 {
102 /* Name too big: alloc a buffer for it */
103 if (!(lpszName = HeapAlloc( GetProcessHeap(), 0, dwMaxLen*sizeof(CHAR))))
104 {
105 ret = ERROR_NOT_ENOUGH_MEMORY;
106 goto cleanup;
107 }
108 }
109
110
111 /* Recursively delete all the subkeys */
112 while (TRUE)
113 {
114 dwSize = dwMaxLen;
115 if (RegEnumKeyExA(hSubKey, 0, lpszName, &dwSize, NULL,
116 NULL, NULL, NULL)) break;
117
118 ret = mru_RegDeleteTreeA(hSubKey, lpszName);
119 if (ret) goto cleanup;
120 }
121
122 if (lpszSubKey)
123 ret = RegDeleteKeyA(hKey, lpszSubKey);
124 else
125 while (TRUE)
126 {
127 dwSize = dwMaxLen;
128 if (RegEnumValueA(hKey, 0, lpszName, &dwSize,
129 NULL, NULL, NULL, NULL)) break;
130
131 ret = RegDeleteValueA(hKey, lpszName);
132 if (ret) goto cleanup;
133 }
134
135 cleanup:
136 /* Free buffer if allocated */
137 if (lpszName != szNameBuf)
138 HeapFree( GetProcessHeap(), 0, lpszName);
139 if(lpszSubKey)
140 RegCloseKey(hSubKey);
141 return ret;
142 }
143
144 static BOOL create_reg_entries(void)
145 {
146 HKEY hKey = NULL;
147
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;
151 RegCloseKey(hKey);
152 return TRUE;
153 }
154
155 static void delete_reg_entries(void)
156 {
157 HKEY hKey;
158
159 if (RegOpenKeyExA(HKEY_CURRENT_USER, REG_TEST_BASEKEYA, 0, KEY_ALL_ACCESS,
160 &hKey))
161 return;
162 mru_RegDeleteTreeA(hKey, REG_TEST_BASESUBKEYA);
163 RegCloseKey(hKey);
164 }
165
166 static void check_reg_entries(const char *mrulist, const char**items)
167 {
168 char buff[128];
169 HKEY hKey = NULL;
170 DWORD type, size, ret;
171 unsigned int i;
172
173 ok(!RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_FULLKEY, &hKey),
174 "Couldn't open test key \"%s\"\n", REG_TEST_FULLKEY);
175 if (!hKey) return;
176
177 type = REG_SZ;
178 size = sizeof(buff);
179 buff[0] = '\0';
180 ret = RegQueryValueExA(hKey, "MRUList", NULL, &type, (LPBYTE)buff, &size);
181
182 ok(!ret && buff[0], "Checking MRU: got %d from RegQueryValueExW\n", ret);
183 if(ret || !buff[0]) return;
184
185 ok(strcmp(buff, mrulist) == 0, "Checking MRU: Expected list %s, got %s\n",
186 mrulist, buff);
187 if(strcmp(buff, mrulist)) return;
188
189 for (i = 0; i < strlen(mrulist); i++)
190 {
191 char name[2];
192 name[0] = mrulist[i];
193 name[1] = '\0';
194 type = REG_SZ;
195 size = sizeof(buff);
196 buff[0] = '\0';
197 ret = RegQueryValueExA(hKey, name, NULL, &type, (LPBYTE)buff, &size);
198 ok(!ret && buff[0],
199 "Checking MRU item %d ('%c'): got %d from RegQueryValueExW\n",
200 i, mrulist[i], ret);
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']);
205 }
206 }
207
208 static INT CALLBACK cmp_mru_strA(LPCVOID data1, LPCVOID data2)
209 {
210 return lstrcmpiA(data1, data2);
211 }
212
213 static HANDLE create_mruA(HKEY hKey, DWORD flags, PROC cmp)
214 {
215 mruA.dwFlags = flags;
216 mruA.lpfnCompare = cmp;
217 mruA.hKey = hKey;
218
219 SetLastError(0);
220 return pCreateMRUListA(&mruA);
221 }
222
223 static void test_MRUListA(void)
224 {
225 const char *checks[LIST_SIZE+1];
226 HANDLE hMRU;
227 HKEY hKey;
228 INT iRet;
229
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);
234
235 if (!pCreateMRUListA || !pFreeMRUList || !pAddMRUStringA || !pEnumMRUList)
236 {
237 skip("MRU entry points not found\n");
238 return;
239 }
240
241 if (0)
242 {
243 /* Create (NULL) - crashes native */
244 hMRU = pCreateMRUListA(NULL);
245 }
246
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);
254
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);
262
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());
268
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;
276
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);
280 if (!hKey)
281 return;
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());
286
287 if (hMRU)
288 {
289 char buffer[255];
290 checks[0] = "Test 1";
291 checks[1] = "Test 2";
292 checks[2] = "Test 3";
293 checks[3] = "Test 4";
294
295 /* Add (NULL list) */
296 SetLastError(0);
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());
301
302 /* Add (NULL string) */
303 if (0)
304 {
305 /* Some native versions crash when passed NULL or fail to SetLastError() */
306 SetLastError(0);
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());
311 }
312
313 /* Add 3 strings. Check the registry is correct after each add */
314 SetLastError(0);
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);
320
321 SetLastError(0);
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);
327
328 SetLastError(0);
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);
334
335 /* Add a duplicate of the 2nd string - it should move to the front,
336 * but keep the same index in the registry.
337 */
338 SetLastError(0);
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);
344
345 /* Add a new string - replaces the oldest string + moves to the front */
346 SetLastError(0);
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);
353
354 /* NULL buffer = get list size */
355 iRet = pEnumMRUList(hMRU, 0, NULL, 0);
356 ok(iRet == 3, "EnumMRUList expected %d, got %d\n", LIST_SIZE, iRet);
357
358 /* negative item pos = get list size */
359 iRet = pEnumMRUList(hMRU, -1, NULL, 0);
360 ok(iRet == 3, "EnumMRUList expected %d, got %d\n", LIST_SIZE, iRet);
361
362 /* negative item pos = get list size */
363 iRet = pEnumMRUList(hMRU, -5, NULL, 0);
364 ok(iRet == 3, "EnumMRUList expected %d, got %d\n", LIST_SIZE, iRet);
365
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);
369
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);
373
374 /* check entry 0 */
375 buffer[0] = 0;
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);
379
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]);
390
391 /* check entry 1 */
392 buffer[0] = 0;
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);
396
397 /* check entry 2 */
398 buffer[0] = 0;
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);
402
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);
408
409 /* Finished with this MRU */
410 pFreeMRUList(hMRU);
411 }
412
413 /* Free (NULL list) - Doesn't crash */
414 pFreeMRUList(NULL);
415 }
416
417 START_TEST(mru)
418 {
419 hComctl32 = GetModuleHandleA("comctl32.dll");
420
421 /* The registry usage here crashes the system because of broken Cm -- remove this when Cm gets fixed */
422 skip("ROS-HACK: Skipping mru tests -- Cm is broken\n");
423 return;
424
425 delete_reg_entries();
426 if (!create_reg_entries())
427 return;
428
429 test_MRUListA();
430
431 delete_reg_entries();
432 }