[SHELL32_APITEST] Improve DoAction8 of SHChangeNotify testcase
[reactos.git] / modules / rostests / apitests / shell32 / SHChangeNotify.cpp
1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
4 * PURPOSE: Test for SHChangeNotify
5 * COPYRIGHT: Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6 */
7
8 #include "shelltest.h"
9 #include <shlwapi.h>
10 #include <stdio.h>
11 #include "SHChangeNotify.h"
12
13 #define DONT_SEND 0x24242424
14
15 static HWND s_hwnd = NULL;
16 static const WCHAR s_szName[] = L"SHChangeNotify testcase";
17 static WCHAR s_szSubProgram[MAX_PATH];
18
19 typedef void (*ACTION)(void);
20
21 typedef struct TEST_ENTRY
22 {
23 INT line;
24 LONG event;
25 LPCVOID item1;
26 LPCVOID item2;
27 LPCSTR pattern;
28 ACTION action;
29 LPCWSTR path1;
30 LPCWSTR path2;
31 } TEST_ENTRY;
32
33 static BOOL
34 DoCreateEmptyFile(LPCWSTR pszFileName)
35 {
36 FILE *fp = _wfopen(pszFileName, L"wb");
37 fclose(fp);
38 return fp != NULL;
39 }
40
41 static void
42 DoAction1(void)
43 {
44 ok_int(CreateDirectoryW(s_dir2, NULL), TRUE);
45 }
46
47 static void
48 DoAction2(void)
49 {
50 ok_int(RemoveDirectoryW(s_dir2), TRUE);
51 }
52
53 static void
54 DoAction3(void)
55 {
56 ok_int(MoveFileExW(s_dir2, s_dir3, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING), TRUE);
57 }
58
59 static void
60 DoAction4(void)
61 {
62 ok_int(DoCreateEmptyFile(s_file1), TRUE);
63 }
64
65 static void
66 DoAction5(void)
67 {
68 ok_int(MoveFileExW(s_file1, s_file2, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING), TRUE);
69 }
70
71 static void
72 DoAction6(void)
73 {
74 ok_int(DeleteFileW(s_file2), TRUE);
75 }
76
77 static void
78 DoAction7(void)
79 {
80 DeleteFileW(s_file1);
81 DeleteFileW(s_file2);
82 ok_int(RemoveDirectoryW(s_dir3), TRUE);
83 }
84
85 static void
86 DoAction8(void)
87 {
88 BOOL ret = RemoveDirectoryW(s_dir1);
89 ok(ret, "RemoveDirectoryW failed. GetLastError() == %ld\n", GetLastError());
90 }
91
92 static const TEST_ENTRY s_TestEntriesMode0[] =
93 {
94 {__LINE__, SHCNE_MKDIR, s_dir1, NULL, "000100000", NULL, s_dir1, L""},
95 {__LINE__, SHCNE_MKDIR, s_dir2, NULL, "000100000", NULL, s_dir2, L""},
96 {__LINE__, SHCNE_RMDIR, s_dir2, NULL, "000010000", NULL, s_dir2, L""},
97 {__LINE__, SHCNE_MKDIR, s_dir2, NULL, "000100000", DoAction1, s_dir2, L""},
98 {__LINE__, SHCNE_RMDIR, s_dir2, NULL, "000010000", NULL, s_dir2, L""},
99 {__LINE__, SHCNE_RMDIR, s_dir2, NULL, "000010000", DoAction2, s_dir2, L""},
100 {__LINE__, SHCNE_MKDIR, s_dir2, NULL, "000100000", DoAction1, s_dir2, L""},
101 {__LINE__, SHCNE_RENAMEFOLDER, s_dir2, s_dir3, "000000010", NULL, s_dir2, s_dir3},
102 {__LINE__, SHCNE_RENAMEFOLDER, s_dir2, s_dir3, "000000010", DoAction3, s_dir2, s_dir3},
103 {__LINE__, SHCNE_CREATE, s_file1, NULL, "010000000", NULL, s_file1, L""},
104 {__LINE__, SHCNE_CREATE, s_file1, s_file2, "010000000", NULL, s_file1, s_file2},
105 {__LINE__, SHCNE_CREATE, s_file1, NULL, "010000000", DoAction4, s_file1, L""},
106 {__LINE__, SHCNE_RENAMEITEM, s_file1, s_file2, "100000000", NULL, s_file1, s_file2},
107 {__LINE__, SHCNE_RENAMEITEM, s_file1, s_file2, "100000000", DoAction5, s_file1, s_file2},
108 {__LINE__, SHCNE_RENAMEITEM, s_file1, s_file2, "100000000", NULL, s_file1, s_file2},
109 {__LINE__, SHCNE_UPDATEITEM, s_file1, NULL, "000000100", NULL, s_file1, L""},
110 {__LINE__, SHCNE_UPDATEITEM, s_file2, NULL, "000000100", NULL, s_file2, L""},
111 {__LINE__, SHCNE_UPDATEITEM, s_file1, s_file2, "000000100", NULL, s_file1, s_file2},
112 {__LINE__, SHCNE_UPDATEITEM, s_file2, s_file1, "000000100", NULL, s_file2, s_file1},
113 {__LINE__, SHCNE_DELETE, s_file1, NULL, "001000000", NULL, s_file1, L""},
114 {__LINE__, SHCNE_DELETE, s_file2, NULL, "001000000", NULL, s_file2, L""},
115 {__LINE__, SHCNE_DELETE, s_file2, s_file1, "001000000", NULL, s_file2, s_file1},
116 {__LINE__, SHCNE_DELETE, s_file1, s_file2, "001000000", NULL, s_file1, s_file2},
117 {__LINE__, SHCNE_DELETE, s_file2, NULL, "001000000", DoAction6, s_file2, L""},
118 {__LINE__, SHCNE_DELETE, s_file2, NULL, "001000000", NULL, s_file2, L""},
119 {__LINE__, SHCNE_DELETE, s_file1, NULL, "001000000", NULL, s_file1, L""},
120 {__LINE__, SHCNE_UPDATEDIR, s_file1, NULL, "000001000", NULL, s_file1, L""},
121 {__LINE__, SHCNE_UPDATEDIR, s_file2, NULL, "000001000", NULL, s_file2, L""},
122 {__LINE__, SHCNE_UPDATEDIR, s_file1, s_file2, "000001000", NULL, s_file1, s_file2},
123 {__LINE__, SHCNE_UPDATEDIR, s_file2, s_file1, "000001000", NULL, s_file2, s_file1},
124 {__LINE__, SHCNE_UPDATEDIR, s_dir1, NULL, "000001000", NULL, s_dir1, L""},
125 {__LINE__, SHCNE_UPDATEDIR, s_dir2, NULL, "000001000", NULL, s_dir2, L""},
126 {__LINE__, SHCNE_UPDATEDIR, s_dir1, s_dir2, "000001000", NULL, s_dir1, s_dir2},
127 {__LINE__, SHCNE_UPDATEDIR, s_dir2, s_dir1, "000001000", NULL, s_dir2, s_dir1},
128 {__LINE__, SHCNE_RMDIR, s_dir1, NULL, "000010000", NULL, s_dir1, L""},
129 {__LINE__, SHCNE_RMDIR, s_dir2, NULL, "000010000", NULL, s_dir2, L""},
130 {__LINE__, SHCNE_RMDIR, s_dir3, NULL, "000010000", NULL, s_dir3, L""},
131 {__LINE__, SHCNE_RMDIR, s_dir1, s_dir2, "000010000", NULL, s_dir1, s_dir2},
132 {__LINE__, SHCNE_RMDIR, s_dir1, s_dir3, "000010000", NULL, s_dir1, s_dir3},
133 {__LINE__, SHCNE_RMDIR, s_dir2, s_dir1, "000010000", NULL, s_dir2, s_dir1},
134 {__LINE__, SHCNE_RMDIR, s_dir2, s_dir3, "000010000", NULL, s_dir2, s_dir3},
135 {__LINE__, SHCNE_RMDIR, s_dir3, NULL, "000010000", NULL, s_dir3, L""},
136 {__LINE__, SHCNE_RMDIR, s_dir3, NULL, "000010000", DoAction7, s_dir3, L""},
137 {__LINE__, SHCNE_RMDIR, s_dir1, NULL, "000010000", NULL, s_dir1, L""},
138 {__LINE__, SHCNE_RMDIR, s_dir1, NULL, "000010000", DoAction8, s_dir1, L""},
139 };
140
141 #define s_TestEntriesMode1 s_TestEntriesMode0
142 #define s_TestEntriesMode2 s_TestEntriesMode0
143
144 LPCSTR PatternFromFlags(DWORD flags)
145 {
146 static char s_buf[TYPE_FREESPACE + 1 + 1];
147 DWORD i;
148 for (i = 0; i <= TYPE_FREESPACE; ++i)
149 {
150 s_buf[i] = (char)('0' + !!(flags & (1 << i)));
151 }
152 s_buf[i] = 0;
153 return s_buf;
154 }
155
156 static BOOL
157 DoGetClipText(LPWSTR pszPath1, LPWSTR pszPath2)
158 {
159 pszPath1[0] = pszPath2[0] = 0;
160
161 if (!IsClipboardFormatAvailable(CF_UNICODETEXT))
162 return FALSE;
163
164 if (!OpenClipboard(NULL))
165 return FALSE;
166
167 WCHAR szText[MAX_PATH * 2];
168 HGLOBAL hGlobal = GetClipboardData(CF_UNICODETEXT);
169 LPWSTR psz = (LPWSTR)GlobalLock(hGlobal);
170 lstrcpynW(szText, psz, _countof(szText));
171 GlobalUnlock(hGlobal);
172 CloseClipboard();
173
174 LPWSTR pch = wcschr(szText, L'|');
175 if (pch == NULL)
176 return FALSE;
177
178 *pch = 0;
179 lstrcpynW(pszPath1, szText, MAX_PATH);
180 lstrcpynW(pszPath2, pch + 1, MAX_PATH);
181 return TRUE;
182 }
183
184 static void
185 DoTestEntry(const TEST_ENTRY *entry)
186 {
187 if (entry->action)
188 {
189 (*entry->action)();
190 }
191
192 if (entry->event != DONT_SEND)
193 {
194 SHChangeNotify(entry->event, SHCNF_PATHW | SHCNF_FLUSH, entry->item1, entry->item2);
195 }
196
197 DWORD flags = SendMessageW(s_hwnd, WM_GET_NOTIFY_FLAGS, 0, 0);
198 LPCSTR pattern = PatternFromFlags(flags);
199
200 ok(lstrcmpA(pattern, entry->pattern) == 0, "Line %d: pattern mismatch '%s'\n", entry->line, pattern);
201
202 SendMessageW(s_hwnd, WM_SET_PATHS, 0, 0);
203
204 WCHAR szPath1[MAX_PATH], szPath2[MAX_PATH];
205 BOOL bOK = DoGetClipText(szPath1, szPath2);
206 if (entry->path1)
207 ok(bOK && lstrcmpiW(entry->path1, szPath1) == 0,
208 "Line %d: path1 mismatch '%S' (%d)\n", entry->line, szPath1, bOK);
209 if (entry->path2)
210 ok(bOK && lstrcmpiW(entry->path2, szPath2) == 0,
211 "Line %d: path2 mismatch '%S' (%d)\n", entry->line, szPath2, bOK);
212
213 SendMessageW(s_hwnd, WM_CLEAR_FLAGS, 0, 0);
214 }
215
216 static BOOL
217 DoInit(void)
218 {
219 DoInitPaths();
220
221 CreateDirectoryW(s_dir1, NULL);
222
223 return PathIsDirectoryW(s_dir1);
224 }
225
226 static void
227 DoEnd(HWND hwnd)
228 {
229 DeleteFileW(s_file1);
230 DeleteFileW(s_file2);
231 RemoveDirectoryW(s_dir3);
232 RemoveDirectoryW(s_dir2);
233 RemoveDirectoryW(s_dir1);
234
235 SendMessageW(s_hwnd, WM_COMMAND, IDOK, 0);
236 }
237
238 static BOOL
239 GetSubProgramPath(void)
240 {
241 GetModuleFileNameW(NULL, s_szSubProgram, _countof(s_szSubProgram));
242 PathRemoveFileSpecW(s_szSubProgram);
243 PathAppendW(s_szSubProgram, L"shell-notify.exe");
244
245 if (!PathFileExistsW(s_szSubProgram))
246 {
247 PathRemoveFileSpecW(s_szSubProgram);
248 PathAppendW(s_szSubProgram, L"testdata\\shell-notify.exe");
249
250 if (!PathFileExistsW(s_szSubProgram))
251 {
252 return FALSE;
253 }
254 }
255
256 return TRUE;
257 }
258
259 static void
260 JustDoIt(INT nMode)
261 {
262 if (!DoInit())
263 {
264 skip("Unable to initialize.\n");
265 return;
266 }
267
268 WCHAR szParams[8];
269 wsprintfW(szParams, L"%u", nMode);
270
271 HINSTANCE hinst = ShellExecuteW(NULL, NULL, s_szSubProgram, szParams, NULL, SW_SHOWNORMAL);
272 if ((INT_PTR)hinst <= 32)
273 {
274 skip("Unable to run shell-notify.exe.\n");
275 return;
276 }
277
278 for (int i = 0; i < 15; ++i)
279 {
280 s_hwnd = FindWindowW(s_szName, s_szName);
281 if (s_hwnd)
282 break;
283
284 Sleep(50);
285 }
286
287 if (!s_hwnd)
288 {
289 skip("Unable to find window.\n");
290 return;
291 }
292
293 switch (nMode)
294 {
295 case 0:
296 case 1:
297 case 2:
298 for (size_t i = 0; i < _countof(s_TestEntriesMode0); ++i)
299 {
300 DoTestEntry(&s_TestEntriesMode0[i]);
301 }
302 break;
303 }
304
305 DoEnd(s_hwnd);
306
307 for (int i = 0; i < 15; ++i)
308 {
309 s_hwnd = FindWindowW(s_szName, s_szName);
310 if (!s_hwnd)
311 break;
312
313 Sleep(50);
314 }
315 }
316
317 START_TEST(SHChangeNotify)
318 {
319 if (!GetSubProgramPath())
320 {
321 skip("shell-notify.exe not found\n");
322 }
323
324 JustDoIt(0);
325 JustDoIt(1);
326 JustDoIt(2);
327 }