[SHELL32_APITEST] Add DragDrop testcase (#2023)
[reactos.git] / modules / rostests / apitests / shell32 / DragDrop.cpp
1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory
4 * PURPOSE: Test for Drag & Drop
5 * PROGRAMMER: Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6 */
7
8 #include "shelltest.h"
9 #include <shlwapi.h>
10
11 #define NDEBUG
12 #include <debug.h>
13 #include <stdio.h>
14
15 #define TESTFILENAME L"DragDropTest.txt"
16 #define DROPPED_ON_FILE L"DragDroppedOn.lnk"
17
18 static CComPtr<IShellFolder> s_pDesktop;
19
20 static WCHAR s_szSrcTestFile[MAX_PATH];
21 static WCHAR s_szDestFolder[MAX_PATH];
22 static WCHAR s_szDestTestFile[MAX_PATH];
23 static WCHAR s_szDestLinkSpec[MAX_PATH];
24 static WCHAR s_szDroppedToItem[MAX_PATH];
25
26 enum OP
27 {
28 OP_NONE,
29 OP_COPY,
30 OP_MOVE,
31 OP_LINK,
32 OP_NONE_OR_COPY,
33 OP_NONE_OR_MOVE,
34 OP_NONE_OR_LINK
35 };
36
37 #define D_NONE DROPEFFECT_NONE
38 #define D_COPY DROPEFFECT_COPY
39 #define D_MOVE DROPEFFECT_MOVE
40 #define D_LINK DROPEFFECT_LINK
41 #define D_NONE_OR_COPY 0xAABBCCDD
42 #define D_NONE_OR_MOVE 0x11223344
43 #define D_NONE_OR_LINK 0x55667788
44
45 struct TEST_ENTRY
46 {
47 int line;
48 OP op;
49 HRESULT hr1;
50 HRESULT hr2;
51 DWORD dwKeyState;
52 DWORD dwEffects1;
53 DWORD dwEffects2;
54 DWORD dwEffects3;
55 };
56
57 static const TEST_ENTRY s_TestEntries[] =
58 {
59 // MK_LBUTTON
60 { __LINE__, OP_NONE, S_OK, S_OK, MK_LBUTTON, D_NONE, D_NONE, D_NONE },
61 { __LINE__, OP_COPY, S_OK, S_OK, MK_LBUTTON, D_COPY, D_COPY, D_COPY },
62 { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON, D_COPY | D_MOVE, D_MOVE, D_NONE },
63 { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON, D_MOVE, D_MOVE, D_NONE },
64 { __LINE__, OP_LINK, S_OK, S_OK, MK_LBUTTON, D_LINK, D_LINK, D_LINK },
65
66 // MK_LBUTTON | MK_SHIFT
67 { __LINE__, OP_NONE, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_NONE, D_NONE, D_NONE },
68 { __LINE__, OP_NONE_OR_COPY, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_COPY, D_NONE_OR_COPY, D_NONE_OR_COPY },
69 { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_COPY | D_MOVE, D_MOVE, D_NONE },
70 { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_MOVE, D_MOVE, D_NONE },
71 { __LINE__, OP_NONE_OR_LINK, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_LINK, D_NONE_OR_LINK, D_NONE_OR_LINK },
72
73 // MK_LBUTTON | MK_SHIFT | MK_CONTROL
74 #define MK_LBUTTON_SHIFT_CTRL (MK_LBUTTON | MK_SHIFT | MK_CONTROL)
75 { __LINE__, OP_NONE, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_NONE, D_NONE, D_NONE },
76 { __LINE__, OP_NONE_OR_COPY, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_COPY, D_NONE_OR_COPY, D_NONE_OR_COPY },
77 { __LINE__, OP_NONE_OR_COPY, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_COPY | D_MOVE, D_NONE_OR_COPY, D_NONE_OR_COPY },
78 { __LINE__, OP_NONE_OR_MOVE, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_MOVE, D_NONE_OR_MOVE, D_NONE_OR_MOVE },
79 { __LINE__, OP_LINK, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_LINK, D_LINK, D_LINK },
80 #undef MK_LBUTTON_SHIFT_CTRL
81
82 // MK_LBUTTON | MK_CONTROL
83 { __LINE__, OP_NONE, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_NONE, D_NONE, D_NONE },
84 { __LINE__, OP_COPY, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_COPY, D_COPY, D_COPY },
85 { __LINE__, OP_COPY, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_COPY | D_MOVE, D_COPY, D_COPY },
86 { __LINE__, OP_NONE_OR_MOVE, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_MOVE, D_NONE_OR_MOVE, D_NONE_OR_MOVE },
87 { __LINE__, OP_NONE_OR_LINK, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_LINK, D_NONE_OR_LINK, D_NONE_OR_LINK },
88 };
89
90 static void DoCreateTestFile(LPCWSTR pszFileName)
91 {
92 FILE *fp = _wfopen(pszFileName, L"wb");
93 ok(fp != NULL, "fp is NULL for '%S'\n", pszFileName);
94 fclose(fp);
95 }
96
97 HRESULT DoCreateShortcut(
98 LPCWSTR pszLnkFileName,
99 LPCWSTR pszTargetPathName)
100 {
101 CComPtr<IPersistFile> ppf;
102 CComPtr<IShellLinkW> psl;
103 HRESULT hr;
104
105 hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
106 IID_IShellLinkW, (LPVOID *)&psl);
107 if (SUCCEEDED(hr))
108 {
109 psl->SetPath(pszTargetPathName);
110
111 hr = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf);
112 if (SUCCEEDED(hr))
113 {
114 hr = ppf->Save(pszLnkFileName, TRUE);
115 }
116 }
117
118 return hr;
119 }
120
121 static HRESULT
122 GetUIObjectOfAbsPidl(PIDLIST_ABSOLUTE pidl, REFIID riid, LPVOID *ppvOut)
123 {
124 *ppvOut = NULL;
125
126 LPCITEMIDLIST pidlLast;
127 CComPtr<IShellFolder> psf;
128 HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (LPVOID *)&psf,
129 &pidlLast);
130 if (FAILED(hr))
131 return hr;
132
133 hr = psf->GetUIObjectOf(NULL, 1, &pidlLast, riid, NULL, ppvOut);
134 return hr;
135 }
136
137 static HRESULT
138 GetUIObjectOfPath(LPCWSTR pszPath, REFIID riid, LPVOID *ppvOut)
139 {
140 *ppvOut = NULL;
141
142 PIDLIST_ABSOLUTE pidl = ILCreateFromPathW(pszPath);
143 if (!pidl)
144 return E_FAIL;
145
146 HRESULT hr = GetUIObjectOfAbsPidl(pidl, riid, ppvOut);
147
148 CoTaskMemFree(pidl);
149
150 return hr;
151 }
152
153 BOOL DoSpecExistsW(LPCWSTR pszSpec)
154 {
155 WIN32_FIND_DATAW find;
156 HANDLE hFind = FindFirstFileW(pszSpec, &find);
157 if (hFind != INVALID_HANDLE_VALUE)
158 {
159 FindClose(hFind);
160 return TRUE;
161 }
162 return FALSE;
163 }
164
165 void DoDeleteSpecW(LPCWSTR pszSpec)
166 {
167 WCHAR szPath[MAX_PATH], szFile[MAX_PATH];
168 lstrcpyW(szPath, pszSpec);
169 PathRemoveFileSpecW(szPath);
170
171 WIN32_FIND_DATAW find;
172 HANDLE hFind = FindFirstFileW(pszSpec, &find);
173 if (hFind != INVALID_HANDLE_VALUE)
174 {
175 do
176 {
177 lstrcpyW(szFile, szPath);
178 PathAppendW(szFile, find.cFileName);
179 DeleteFileW(szFile);
180 } while (FindNextFileW(hFind, &find));
181
182 FindClose(hFind);
183 }
184 }
185
186 static void DoTestEntry(const TEST_ENTRY *pEntry)
187 {
188 int line = pEntry->line;
189 HRESULT hr;
190 PIDLIST_ABSOLUTE pidlDesktop = NULL;
191 CComPtr<IDropTarget> pDropTarget;
192 CComPtr<IDataObject> pDataObject;
193
194 // get the desktop PIDL
195 SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidlDesktop);
196 ok(!!pidlDesktop, "pidlDesktop is NULL\n");
197
198 // build paths
199 //
200 SHGetPathFromIDListW(pidlDesktop, s_szDroppedToItem);
201 PathAppendW(s_szDroppedToItem, DROPPED_ON_FILE);
202
203 GetModuleFileNameW(NULL, s_szSrcTestFile, _countof(s_szSrcTestFile));
204 PathRemoveFileSpecW(s_szSrcTestFile);
205 PathAppendW(s_szSrcTestFile, TESTFILENAME);
206
207 lstrcpyW(s_szDestTestFile, s_szDestFolder);
208 PathAppendW(s_szDestTestFile, TESTFILENAME);
209
210 lstrcpyW(s_szDestLinkSpec, s_szDestFolder);
211 PathAppendW(s_szDestLinkSpec, L"*DragDropTest*.lnk");
212
213 //trace("s_szSrcTestFile: '%S'\n", s_szSrcTestFile);
214 //trace("s_szDestTestFile: '%S'\n", s_szDestTestFile);
215 //trace("s_szDestLinkSpec: '%S'\n", s_szDestLinkSpec);
216 //trace("s_szDroppedToItem: '%S'\n", s_szDroppedToItem);
217
218 // create or delete files
219 //
220 DoCreateTestFile(s_szSrcTestFile);
221 DeleteFileW(s_szDestTestFile);
222 DoDeleteSpecW(s_szDestLinkSpec);
223 DeleteFileW(s_szDroppedToItem);
224 DoCreateShortcut(s_szDroppedToItem, s_szDestFolder);
225
226 // check file existence
227 //
228 ok(PathIsDirectoryW(s_szDestFolder), "s_szDestFolder is not directory\n");
229 ok(PathFileExistsW(s_szSrcTestFile), "s_szSrcTestFile doesn't exist\n");
230 ok(!DoSpecExistsW(s_szDestLinkSpec), "s_szDestLinkSpec doesn't exist\n");
231 ok(!PathFileExistsW(s_szDestTestFile), "s_szDestTestFile exists\n");
232
233 // get an IDataObject
234 pDataObject = NULL;
235 hr = GetUIObjectOfPath(s_szSrcTestFile, IID_IDataObject, (LPVOID *)&pDataObject);
236 ok_long(hr, S_OK);
237
238 // get an IDropTarget
239 CComPtr<IEnumIDList> pEnumIDList;
240 PIDLIST_ABSOLUTE pidl = NULL;
241 hr = s_pDesktop->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS,
242 &pEnumIDList);
243 ok_long(hr, S_OK);
244 while (pEnumIDList->Next(1, &pidl, NULL) == S_OK)
245 {
246 WCHAR szText[MAX_PATH];
247 SHGetPathFromIDListW(pidl, szText);
248 if (wcsstr(szText, DROPPED_ON_FILE) != NULL)
249 {
250 break;
251 }
252 CoTaskMemFree(pidl);
253 pidl = NULL;
254 }
255 ok(pidl != NULL, "pidl is NULL\n");
256 pDropTarget = NULL;
257 PITEMID_CHILD pidlLast = ILFindLastID(pidl);
258 hr = s_pDesktop->GetUIObjectOf(NULL, 1, &pidlLast, IID_IDropTarget,
259 NULL, (LPVOID *)&pDropTarget);
260 CoTaskMemFree(pidl);
261 ok_long(hr, S_OK);
262
263 // DragEnter
264 POINTL ptl = { 0, 0 };
265 DWORD dwKeyState = pEntry->dwKeyState;
266 DWORD dwEffects = pEntry->dwEffects1;
267 hr = pDropTarget->DragEnter(pDataObject, dwKeyState, ptl, &dwEffects);
268
269 ok(hr == pEntry->hr1, "Line %d: hr1 was %08lX\n", line, hr);
270
271 switch (pEntry->dwEffects2)
272 {
273 case D_NONE_OR_COPY:
274 ok((dwEffects == D_NONE || dwEffects == D_COPY),
275 "Line %d: dwEffects2 was %08lX\n", line, dwEffects);
276 break;
277 case D_NONE_OR_MOVE:
278 ok((dwEffects == D_NONE || dwEffects == D_MOVE),
279 "Line %d: dwEffects2 was %08lX\n", line, dwEffects);
280 break;
281 case D_NONE_OR_LINK:
282 ok((dwEffects == D_NONE || dwEffects == D_LINK),
283 "Line %d: dwEffects2 was %08lX\n", line, dwEffects);
284 break;
285 default:
286 ok(dwEffects == pEntry->dwEffects2,
287 "Line %d: dwEffects2 was %08lX\n", line, dwEffects);
288 break;
289 }
290
291 // Drop
292 hr = pDropTarget->Drop(pDataObject, dwKeyState, ptl, &dwEffects);
293 ok(hr == pEntry->hr2, "Line %d: hr2 was %08lX\n", line, hr);
294
295 switch (pEntry->dwEffects3)
296 {
297 case D_NONE_OR_COPY:
298 ok((dwEffects == D_NONE || dwEffects == D_COPY),
299 "Line %d: dwEffects3 was %08lX\n", line, dwEffects);
300 break;
301 case D_NONE_OR_MOVE:
302 ok((dwEffects == D_NONE || dwEffects == D_MOVE),
303 "Line %d: dwEffects3 was %08lX\n", line, dwEffects);
304 break;
305 case D_NONE_OR_LINK:
306 ok((dwEffects == D_NONE || dwEffects == D_LINK),
307 "Line %d: dwEffects3 was %08lX\n", line, dwEffects);
308 break;
309 default:
310 ok(dwEffects == pEntry->dwEffects3,
311 "Line %d: dwEffects3 was %08lX\n", line, dwEffects);
312 break;
313 }
314
315 // check file existence by pEntry->op
316 switch (pEntry->op)
317 {
318 case OP_NONE:
319 ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line);
320 ok(!PathFileExistsW(s_szDestTestFile), "Line %d: dest exists\n", line);
321 ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line);
322 break;
323 case OP_COPY:
324 ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line);
325 ok(PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line);
326 ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line);
327 break;
328 case OP_MOVE:
329 ok(!PathFileExistsW(s_szSrcTestFile), "Line %d: src exists\n", line);
330 ok(PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line);
331 ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line);
332 break;
333 case OP_LINK:
334 ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line);
335 ok(!PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line);
336 ok(DoSpecExistsW(s_szDestLinkSpec), "Line %d: link not exists\n", line);
337 break;
338 case OP_NONE_OR_COPY:
339 ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line);
340 ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line);
341 break;
342 case OP_NONE_OR_MOVE:
343 ok(PathFileExistsW(s_szSrcTestFile) != PathFileExistsW(s_szDestTestFile),
344 "Line %d: It must be either None or Move\n", line);
345 break;
346 case OP_NONE_OR_LINK:
347 ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line);
348 ok(!PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line);
349 break;
350 }
351
352 // clean up
353 DeleteFileW(s_szSrcTestFile);
354 DeleteFileW(s_szDestTestFile);
355 DoDeleteSpecW(s_szDestLinkSpec);
356 ILFree(pidlDesktop);
357 }
358
359 START_TEST(DragDrop)
360 {
361 HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
362 ok_int(SUCCEEDED(hr), TRUE);
363
364 SHGetDesktopFolder(&s_pDesktop);
365 ok(!!s_pDesktop, "s_pDesktop is NULL\n");
366
367 BOOL ret = SHGetSpecialFolderPathW(NULL, s_szDestFolder, CSIDL_DESKTOP, FALSE);
368 ok_int(ret, TRUE);
369
370 for (size_t i = 0; i < _countof(s_TestEntries); ++i)
371 {
372 DoTestEntry(&s_TestEntries[i]);
373 }
374
375 DeleteFileW(s_szSrcTestFile);
376 DeleteFileW(s_szDestTestFile);
377 DoDeleteSpecW(s_szDestLinkSpec);
378 DeleteFileW(s_szDroppedToItem);
379
380 CoUninitialize();
381 }