1 /*
2  * PROJECT:     ReactOS api tests
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Test for IACLCustomMRU objects
5  * COPYRIGHT:   Copyright 2017 Mark Jansen (mark.jansen@reactos.org)
6  */
7 
8 #define _UNICODE
9 #define UNICODE
10 #include <apitest.h>
11 #include <shlobj.h>
12 #include <atlbase.h>
13 #include <atlstr.h>
14 #include <atlcom.h>
15 #include <atlwin.h>
16 
17 // Yes, gcc at it again, let's validate everything found inside unused templates!
18 ULONG DbgPrint(PCH Format,...);
19 
20 #include <shellutils.h>
21 #include <shlwapi.h>
22 #include <strsafe.h>
23 #include <initguid.h>
24 
25 #define ok_hex_(file, line, key, expression, result) \
26     do { \
27         int _value = (expression); \
28         ok_(file, line)(_value == (result), "Wrong value for '%s', expected: " #result " (0x%x), got: 0x%x for %c\n", \
29            #expression, (int)(result), _value, (char)key); \
30     } while (0)
31 
32 
33 
34 struct CCoInit
35 {
36     CCoInit() { hres = CoInitialize(NULL); }
37     ~CCoInit() { if (SUCCEEDED(hres)) { CoUninitialize(); } }
38     HRESULT hres;
39 };
40 
41 
42 DEFINE_GUID(IID_IACLCustomMRU,             0xf729fc5e, 0x8769, 0x4f3e, 0xbd, 0xb2, 0xd7, 0xb5, 0x0f, 0xd2, 0x27, 0x5b);
43 static const WCHAR szTestPath[] = L"TESTPATH_BROWSEUI_APITEST";
44 
45 #undef INTERFACE
46 #define INTERFACE IACLCustomMRU
47 
48 /* based on https://msdn.microsoft.com/en-gb/library/windows/desktop/bb776380(v=vs.85).aspx */
49 DECLARE_INTERFACE_IID_(IACLCustomMRU, IUnknown, "F729FC5E-8769-4F3E-BDB2-D7B50FD2275B")
50 {
51     // *** IUnknown methods ***
52     STDMETHOD(QueryInterface) (THIS_ REFIID riid, void **ppv) PURE;
53     STDMETHOD_(ULONG, AddRef) (THIS)PURE;
54     STDMETHOD_(ULONG, Release) (THIS)PURE;
55 
56     // *** IACLCustomMRU methods ***
57     STDMETHOD(Initialize) (THIS_ LPCWSTR pwszMRURegKey, DWORD dwMax) PURE;
58     STDMETHOD(AddMRUString) (THIS_ LPCWSTR pwszEntry) PURE;
59 };
60 
61 
62 static void Cleanup_Testdata()
63 {
64     CRegKey tmp;
65     if (!tmp.Open(HKEY_CURRENT_USER, NULL, KEY_READ | KEY_WRITE))
66         tmp.DeleteSubKey(szTestPath);
67 }
68 
69 #define verify_mru(mru, ...)     verify_mru_(__FILE__, __LINE__, mru, __VA_ARGS__, NULL)
70 static void verify_mru_(const char* file, int line, IACLCustomMRU* mru, PCWSTR MRUString, ...)
71 {
72 
73     CRegKey key;
74     key.Open(HKEY_CURRENT_USER, szTestPath);
75 
76     va_list args;
77     va_start(args, MRUString);
78     PCWSTR Entry;
79     WCHAR Key = L'a';
80     while ((Entry = va_arg(args, PCWSTR)))
81     {
82         WCHAR Value[MAX_PATH];
83         ULONG nChars = _countof(Value);
84         CStringW tmp;
85         tmp += Key;
86         LSTATUS Status = key.QueryStringValue(tmp, Value, &nChars);
87         ok_hex_(file, line, Key, Status, ERROR_SUCCESS);
88         if (Status == ERROR_SUCCESS)
89         {
90             ok_(file, line)(!wcscmp(Value, Entry), "Expected value %c to be %S, was %S\n", (char)Key, Entry, Value);
91         }
92         Key++;
93     }
94     va_end(args);
95 
96     if (Key != L'a')
97     {
98         WCHAR Value[MAX_PATH];
99         ULONG nChars = _countof(Value);
100         LSTATUS Status = key.QueryStringValue(L"MRUList", Value, &nChars);
101         ok_hex_(file, line, Key, Status, ERROR_SUCCESS);
102         if (Status == ERROR_SUCCESS)
103         {
104             ok_(file, line)(!wcscmp(Value, MRUString), "Expected MRUList to be %S, was %S\n", MRUString, Value);
105         }
106     }
107 }
108 
109 
110 static void
111 test_IACLCustomMRU_Basics()
112 {
113     CComPtr<IACLCustomMRU> CustomMRU;
114     HRESULT hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_ALL, IID_PPV_ARG(IACLCustomMRU, &CustomMRU));
115     ok_hex(hr, S_OK);
116     if (!SUCCEEDED(hr))
117         return;
118 
119     Cleanup_Testdata();
120 
121     /* Initialize with a NULL name will cause an AV */
122     //hr = CustomMRU->Initialize(NULL, 0);
123 
124     hr = CustomMRU->Initialize(szTestPath, 0);
125     ok_hex(hr, S_OK);
126     /* Adding an entry with a dwMax of 0 will cause an AV */
127 
128     /* Calling it again will resize */
129     hr = CustomMRU->Initialize(szTestPath, 3);
130     ok_hex(hr, S_OK);
131     verify_mru(CustomMRU, L"");
132 
133     hr = CustomMRU->AddMRUString(L"FIRST_ENTRY");
134     ok_hex(hr, S_OK);
135     verify_mru(CustomMRU, L"a", L"FIRST_ENTRY");
136 
137     hr = CustomMRU->AddMRUString(L"SECOND_ENTRY");
138     ok_hex(hr, S_OK);
139     verify_mru(CustomMRU, L"ba", L"FIRST_ENTRY", L"SECOND_ENTRY");
140 
141     hr = CustomMRU->AddMRUString(L"THIRD_ENTRY");
142     ok_hex(hr, S_OK);
143     verify_mru(CustomMRU, L"cba", L"FIRST_ENTRY", L"SECOND_ENTRY", L"THIRD_ENTRY");
144 
145     /* First entry is replaced */
146     hr = CustomMRU->AddMRUString(L"FOURTH_ENTRY");
147     ok_hex(hr, S_OK);
148     verify_mru(CustomMRU, L"acb", L"FOURTH_ENTRY", L"SECOND_ENTRY", L"THIRD_ENTRY");
149 
150     /* Second entry is replaced */
151     hr = CustomMRU->AddMRUString(L"FIFTH_ENTRY");
152     ok_hex(hr, S_OK);
153     verify_mru(CustomMRU, L"bac", L"FOURTH_ENTRY", L"FIFTH_ENTRY", L"THIRD_ENTRY");
154 }
155 
156 
157 static void FillDefault(IACLCustomMRU* CustomMRU)
158 {
159     Cleanup_Testdata();
160     HRESULT hr = CustomMRU->Initialize(szTestPath, 3);
161     ok_hex(hr, S_OK);
162     hr = CustomMRU->AddMRUString(L"FIRST_ENTRY");
163     ok_hex(hr, S_OK);
164     hr = CustomMRU->AddMRUString(L"SECOND_ENTRY");
165     ok_hex(hr, S_OK);
166     hr = CustomMRU->AddMRUString(L"THIRD_ENTRY");
167     ok_hex(hr, S_OK);
168 }
169 
170 static void
171 test_IACLCustomMRU_UpdateOrder()
172 {
173     CComPtr<IACLCustomMRU> CustomMRU;
174     HRESULT hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_ALL, IID_PPV_ARG(IACLCustomMRU, &CustomMRU));
175     ok_hex(hr, S_OK);
176     if (!SUCCEEDED(hr))
177         return;
178 
179     Cleanup_Testdata();
180     FillDefault(CustomMRU);
181     verify_mru(CustomMRU, L"cba", L"FIRST_ENTRY", L"SECOND_ENTRY", L"THIRD_ENTRY");
182 
183     /* Add the first entry again */
184     hr = CustomMRU->AddMRUString(L"FIRST_ENTRY");
185     ok_hex(hr, S_OK);
186     /* No change */
187     verify_mru(CustomMRU, L"cba", L"FIRST_ENTRY", L"SECOND_ENTRY", L"THIRD_ENTRY");
188 
189     CustomMRU.Release();
190     /* Now the order is updated */
191     verify_mru(NULL, L"acb", L"FIRST_ENTRY", L"SECOND_ENTRY", L"THIRD_ENTRY");
192 
193 
194     hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_ALL, IID_PPV_ARG(IACLCustomMRU, &CustomMRU));
195     ok_hex(hr, S_OK);
196     if (!SUCCEEDED(hr))
197         return;
198 
199     Cleanup_Testdata();
200     FillDefault(CustomMRU);
201     verify_mru(CustomMRU, L"cba", L"FIRST_ENTRY", L"SECOND_ENTRY", L"THIRD_ENTRY");
202 
203 
204     /* Add the first entry again */
205     hr = CustomMRU->AddMRUString(L"FIRST_ENTRY");
206     ok_hex(hr, S_OK);
207     /* No change */
208     verify_mru(CustomMRU, L"cba", L"FIRST_ENTRY", L"SECOND_ENTRY", L"THIRD_ENTRY");
209 
210     hr = CustomMRU->AddMRUString(L"SOMETHING_ELSE");
211     ok_hex(hr, S_OK);
212     /* Now all changes are persisted */
213     verify_mru(CustomMRU, L"bac", L"FIRST_ENTRY", L"SOMETHING_ELSE", L"THIRD_ENTRY");
214 }
215 
216 static void
217 test_IACLCustomMRU_ExtraChars()
218 {
219     CComPtr<IACLCustomMRU> CustomMRU;
220     HRESULT hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_ALL, IID_PPV_ARG(IACLCustomMRU, &CustomMRU));
221     ok_hex(hr, S_OK);
222     if (!SUCCEEDED(hr))
223         return;
224 
225     Cleanup_Testdata();
226 
227     /* Still returnes success */
228     hr = CustomMRU->Initialize(szTestPath, 30);
229     ok_hex(hr, S_OK);
230 
231     for (int n = 0; n < 30; ++n)
232     {
233         CStringW tmp;
234         tmp.Format(L"%d", n);
235 
236         hr = CustomMRU->AddMRUString(tmp);
237         ok_hex(hr, S_OK);
238     }
239     /* But is starting to wrap around */
240     verify_mru(CustomMRU, L"a}|{zyxwvutsrqponmlkjihgfedcb", L"29",
241                L"1", L"2", L"3", L"4", L"5", L"6", L"7", L"8", L"9",
242                L"10", L"11", L"12", L"13", L"14", L"15", L"16", L"17", L"18", L"19",
243                L"20", L"21", L"22", L"23", L"24", L"25", L"26", L"27", L"28");
244 
245     Cleanup_Testdata();
246 }
247 
248 static void
249 test_IACLCustomMRU_Continue()
250 {
251     CComPtr<IACLCustomMRU> CustomMRU;
252     HRESULT hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_ALL, IID_PPV_ARG(IACLCustomMRU, &CustomMRU));
253     ok_hex(hr, S_OK);
254     if (!SUCCEEDED(hr))
255         return;
256 
257     Cleanup_Testdata();
258     FillDefault(CustomMRU);
259     verify_mru(CustomMRU, L"cba", L"FIRST_ENTRY", L"SECOND_ENTRY", L"THIRD_ENTRY");
260 
261     CustomMRU.Release();
262 
263     hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_ALL, IID_PPV_ARG(IACLCustomMRU, &CustomMRU));
264     ok_hex(hr, S_OK);
265     if (!SUCCEEDED(hr))
266         return;
267 
268     hr = CustomMRU->Initialize(szTestPath, 3);
269     ok_hex(hr, S_OK);
270 
271     /* First entry is replaced */
272     hr = CustomMRU->AddMRUString(L"FOURTH_ENTRY");
273     ok_hex(hr, S_OK);
274     verify_mru(CustomMRU, L"acb", L"FOURTH_ENTRY", L"SECOND_ENTRY", L"THIRD_ENTRY");
275 
276     CustomMRU.Release();
277 
278     hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_ALL, IID_PPV_ARG(IACLCustomMRU, &CustomMRU));
279     ok_hex(hr, S_OK);
280     if (!SUCCEEDED(hr))
281         return;
282 
283     hr = CustomMRU->Initialize(szTestPath, 3);
284     ok_hex(hr, S_OK);
285 
286     /* Second entry is replaced */
287     hr = CustomMRU->AddMRUString(L"FIFTH_ENTRY");
288     ok_hex(hr, S_OK);
289     verify_mru(CustomMRU, L"bac", L"FOURTH_ENTRY", L"FIFTH_ENTRY", L"THIRD_ENTRY");
290 
291     CustomMRU.Release();
292 
293     hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_ALL, IID_PPV_ARG(IACLCustomMRU, &CustomMRU));
294     ok_hex(hr, S_OK);
295     if (!SUCCEEDED(hr))
296         return;
297 
298 
299     /* Save some garbage */
300     CRegKey key;
301     key.Open(HKEY_CURRENT_USER, szTestPath);
302     key.SetStringValue(L"MRUList", L"b**");
303     key.Close();
304 
305     hr = CustomMRU->Initialize(szTestPath, 3);
306     ok_hex(hr, S_OK);
307 
308     CustomMRU.Release();
309 
310     /* Not cleaned up */
311     verify_mru(CustomMRU, L"b**", L"FOURTH_ENTRY", L"FIFTH_ENTRY", L"THIRD_ENTRY");
312 
313     hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_ALL, IID_PPV_ARG(IACLCustomMRU, &CustomMRU));
314     ok_hex(hr, S_OK);
315     if (!SUCCEEDED(hr))
316         return;
317 
318     hr = CustomMRU->Initialize(szTestPath, 3);
319     ok_hex(hr, S_OK);
320 
321     /* Now it's just cleaned up */
322     hr = CustomMRU->AddMRUString(L"SIXTH_ENTRY");
323     ok_hex(hr, S_OK);
324     verify_mru(CustomMRU, L"ab", L"SIXTH_ENTRY");
325 
326     CustomMRU.Release();
327 
328     hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_ALL, IID_PPV_ARG(IACLCustomMRU, &CustomMRU));
329     ok_hex(hr, S_OK);
330     if (!SUCCEEDED(hr))
331         return;
332 
333     Cleanup_Testdata();
334     FillDefault(CustomMRU);
335     verify_mru(CustomMRU, L"cba", L"FIRST_ENTRY", L"SECOND_ENTRY", L"THIRD_ENTRY");
336 
337     CustomMRU.Release();
338 
339     hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_ALL, IID_PPV_ARG(IACLCustomMRU, &CustomMRU));
340     ok_hex(hr, S_OK);
341     if (!SUCCEEDED(hr))
342         return;
343 
344     key.Open(HKEY_CURRENT_USER, szTestPath);
345     key.SetStringValue(L"MRUList", L"baccccc");
346     key.Close();
347 
348     hr = CustomMRU->Initialize(szTestPath, 3);
349     ok_hex(hr, S_OK);
350     CustomMRU.Release();
351 
352     verify_mru(CustomMRU, L"baccccc", L"FIRST_ENTRY", L"SECOND_ENTRY", L"THIRD_ENTRY");
353 
354     hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_ALL, IID_PPV_ARG(IACLCustomMRU, &CustomMRU));
355     ok_hex(hr, S_OK);
356     if (!SUCCEEDED(hr))
357         return;
358 
359     hr = CustomMRU->Initialize(szTestPath, 3);
360     ok_hex(hr, S_OK);
361 
362     hr = CustomMRU->AddMRUString(L"FOURTH_ENTRY");
363     ok_hex(hr, S_OK);
364     verify_mru(CustomMRU, L"a", L"FOURTH_ENTRY", L"SECOND_ENTRY", L"THIRD_ENTRY");
365 
366     CustomMRU.Release();
367     Cleanup_Testdata();
368 
369     hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_ALL, IID_PPV_ARG(IACLCustomMRU, &CustomMRU));
370     ok_hex(hr, S_OK);
371     if (!SUCCEEDED(hr))
372         return;
373 
374     hr = CustomMRU->Initialize(szTestPath, 3);
375     ok_hex(hr, S_OK);
376     if (!SUCCEEDED(hr))
377         return;
378 
379     hr = CustomMRU->AddMRUString(L"FIRST_ENTRY");
380     ok_hex(hr, S_OK);
381     verify_mru(CustomMRU, L"a", L"FIRST_ENTRY");
382 
383     CustomMRU.Release();
384 
385     key.Open(HKEY_CURRENT_USER, szTestPath);
386     key.SetStringValue(L"MRUList", L"aaa");
387     key.Close();
388 
389     hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_ALL, IID_PPV_ARG(IACLCustomMRU, &CustomMRU));
390     ok_hex(hr, S_OK);
391     if (!SUCCEEDED(hr))
392         return;
393 
394     hr = CustomMRU->Initialize(szTestPath, 3);
395     ok_hex(hr, S_OK);
396     if (!SUCCEEDED(hr))
397         return;
398 
399     hr = CustomMRU->AddMRUString(L"SECOND_ENTRY");
400     ok_hex(hr, S_OK);
401     verify_mru(CustomMRU, L"ba", L"FIRST_ENTRY", L"SECOND_ENTRY");
402 }
403 
404 START_TEST(IACLCustomMRU)
405 {
406     CCoInit init;
407     ok_hex(init.hres, S_OK);
408     if (!SUCCEEDED(init.hres))
409         return;
410 
411     test_IACLCustomMRU_Basics();
412     test_IACLCustomMRU_UpdateOrder();
413     test_IACLCustomMRU_ExtraChars();
414     test_IACLCustomMRU_Continue();
415 
416     Cleanup_Testdata();
417 }
418